说明:
这个demo效果很简单,可以直接用EGE的fillpoly相关函数实现,但他代表的意义不同:因为这是你自己画的,所以每一个像素点都为你所控制,能实现像素级的操作(这也是用EGE写3D的必须经过的历程),该项目下一步是实现。
效果图:
一部分在外面是为了测试裁剪
性能:
实测开启优化后,每帧一个渐变三角形亦可稳定在60fps
代码:
如下,因为基本上都封装好了,大家完全可以直接拿DrawTrangle画。
建议留到高度熟悉EGE后详细阅读。
- #include <graphics.h>
- #include <algorithm>
- #include <cmath>
- #include <stdio.h>
- #include <map>
- using namespace std;
- //判断点v是否在[a,b]中
- //C#与JAVa的后遗症,EGEAPP
- namespace EA {
- using namespace std;
- #define ct color_t
- //几个全局变量
- //屏幕buffer
- color_t* wBuffer=nullptr;
- //纹理buffer
- color_t* tBuffer=nullptr;
- //屏幕长宽
- int W=0;
- int H=0;
- inline float F2I(float f) {
- return floor(f);
- }
- //把浮点数位数强定为3位(担心爆精度
- inline float F2F3(float f) {
- return floor(f*1000.0f)/1000.0f;
- }
- template<class T>
- inline bool IsCovered(T a,T b,T v) {
- if(a>b) swap(a,b);
- return (a<=v&&v<=b);
- }
- //数据类型
- class Vertex2D {
- public:
- float x;
- float y;
- ct c;
- inline Vertex2D() {
- x=y=0.0f;
- c=0;
- }
- inline Vertex2D(float _x,float _y,ct _c) {
- x=_x,y=_y,c=_c;
- }
- inline void Format() {
- x=EA::F2I(x);
- y=EA::F2I(y);
- }
- inline VECTOR3D ToVec3D() {
- return VECTOR3D(x,y,0);
- }
- };
- typedef struct Vertex2D Ver2D;
- //函数
- //初始化绘图环境
- void InitGraph(int w,int h) {
- initgraph(w,h,0);
- ege_enable_aa(1);
- W=getwidth(),H=getheight();
- wBuffer=getbuffer(NULL);
- }
- //颜色插值,0<=v<=1
- inline ct ColorLerp(ct c1,ct c2,float v);
- //2D顶点插值
- inline Ver2D V2DLerp(Ver2D v1,Ver2D v2,float t);
- //高速画点
- inline void PutPixel_f(Ver2D v);
- //高速画水平线
- //这个会渐变
- inline void DrawLine_f(float x1,float x2,float y,ct c1,ct c2);
- //绘制简单三角,要求至少两个点y一样
- inline void DrawSimpleTrangle(Ver2D v1,Ver2D v2,Ver2D v3);
- //绘制复杂三角
- inline void DrawTrangle(Ver2D v1,Ver2D v2,Ver2D v3);
- #undef ct
- }
- using EA::Ver2D;
- #define ct color_t
- //程序入口
- int main() {
- EA::InitGraph(1000,600);
- EA::DrawTrangle(Ver2D(0,EA::H/2,RED),Ver2D(EA::W/4,0,GREEN),Ver2D(EA::W/2,EA::H,BLUE));
- delay_fps(60);
- getch();
- closegraph();
- return 0;
- }
- ct EA::ColorLerp(ct c1,ct c2,float v) {
- v=EA::F2F3(v);//可有可无,感觉没啥用
- int r=EGEGET_R(c1)-EGEGET_R(c2);
- int g=EGEGET_G(c1)-EGEGET_G(c2);
- int b=EGEGET_B(c1)-EGEGET_B(c2);
- r=r*v+EGEGET_R(c2);
- g=g*v+EGEGET_G(c2);
- b=b*v+EGEGET_B(c2);
- return EGERGB(r,g,b);
- }
- void EA::PutPixel_f(Ver2D v) {
- v.Format();
- if(EA::IsCovered(0,W,int(v.x))&&EA::IsCovered(0,H,int(v.y))) {
- wBuffer[int(v.x+v.y*W)]=v.c;
- }
- }
- void EA::DrawLine_f(float x1,float x2,float y,ct c1,ct c2) {
- if(x1>x2) swap(x1,x2);
- x1=F2I(x1);
- x2=F2I(x2);
- y=F2I(y);
- for(float i=x1; i<=x2; ++i) {
- PutPixel_f(Vertex2D(i,y,ColorLerp(c1,c2,float((i-x1)/(x2-x1)))));
- }
- }
- //v1.y==v2.y
- void EA::DrawSimpleTrangle(Ver2D v1,Ver2D v2,Ver2D v3) {
- //Format
- if(v2.y==v3.y&&v1.y!=v2.y) {
- swap(v1,v2);
- swap(v2,v3);
- } else if(v1.y==v3.y&&v1.y!=v2.y) {
- swap(v2,v3);
- }
- if(v1.x>v2.x) {
- swap(v1,v2);
- }
- //解一次方程
- float h=v3.y-v1.y;
- float kL=(v3.x-v1.x)/h;
- float kR=(v3.x-v2.x)/h;
- float xL=0;
- float xR=0;
- ct c1=0,c2=0;
- xL=v1.x;
- xR=v2.x;
- if(h>0) {
- for(float i=v1.y; i<=v3.y; ++i) {
- float percent=1.0f-(i-v1.y)/(v3.y-v1.y);
- c1=EA::ColorLerp(v1.c,v3.c,percent);
- c2=EA::ColorLerp(v2.c,v3.c,percent);
- EA::DrawLine_f(xL,xR,i,c1,c2);
- xL+=kL;
- xR+=kR;
- }
- } else {
- for(float i=v1.y; i>=v3.y; --i) {
- float percent=(i-v3.y)/(v1.y-v3.y);
- c1=EA::ColorLerp(v1.c,v3.c,percent);
- c2=EA::ColorLerp(v2.c,v3.c,percent);
- EA::DrawLine_f(xL,xR,i,c1,c2);
- xL-=kL;
- xR-=kR;
- }
- }
- }
- void EA::DrawTrangle(Ver2D v1,Ver2D v2,Ver2D v3) {
- if(v1.y==v2.y||v1.y==v3.y||v2.y==v3.y) {
- EA::DrawSimpleTrangle(v1,v2,v3);
- } else {
- //找出y中间的顶点,并放在v3的位置
- if(v2.y<v1.y&&v1.y<v3.y) {
- swap(v1,v3);
- } else if(v1.y<v2.y&&v2.y<v3.y) {
- swap(v2,v3);
- }
- //切成两半
- float h=v1.y-v2.y;
- float w=v1.x-v2.x;
- Ver2D vm((w/h)*(v3.y-v2.y)+v2.x,v3.y,EA::ColorLerp(v2.c,v1.c,(v3.y-v2.y)/h));
- swap(vm.c,v3.c);
- //画他
- DrawSimpleTrangle(v1,vm,v3);
- DrawSimpleTrangle(v2,vm,v3);
- }
- }
- #undef ct
项目:EGE光栅化