EGE光栅化三角形(无纹理)

说明:

这个demo效果很简单,可以直接用EGE的fillpoly相关函数实现,但他代表的意义不同:因为这是你自己画的,所以每一个像素点都为你所控制,能实现像素级的操作(这也是用EGE写3D的必须经过的历程),该项目下一步是实现。

效果图:

一部分在外面是为了测试裁剪

QQ截图20200312233429.png

性能:

实测开启优化后,每帧一个渐变三角形亦可稳定在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