说明:
这个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光栅化