承接上次的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
PIMAGE img;
color_t* tBuffer=nullptr;
//屏幕长宽
int W=0;
int H=0;
//纹理宽
int tW=0;
int tH=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;
class Point2D{
public:
Vertex2D xy;//位置
Vertex2D uv;//纹理坐标
Point2D(float x,float y,float u,float v){
xy.x=x;xy.y=y;
uv.x=u;uv.y=v;
}
Point2D(){
}
};
//函数
//初始化绘图环境
void InitGraph(int w,int h,string image) {
initgraph(w,h,0);
ege_enable_aa(1);
W=getwidth(),H=getheight();
img=newimage();
getimage(img,image.c_str());
ege_enable_aa(1,img);
tW=getwidth(img);
tH=getheight(img);
wBuffer=getbuffer(NULL);
tBuffer=getbuffer(img);
}
//向量插值,0<=v<=1
inline Point2D VerLerp(Point2D c1,Point2D c2,float v);
//高速画点
inline void PutPixel_f(Point2D v);
//高速画水平线
//这个会渐变
inline void DrawLine_f(float x1,float x2,float y,float u1,float u2,float v);
//绘制简单三角,要求至少两个点y一样
inline void DrawSimpleTrangle(Point2D v1,Point2D v2,Point2D v3);
//绘制复杂三角
inline void DrawTrangle(Point2D v1,Point2D v2,Point2D v3);
#undef ct
}
using EA::Point2D;
#define ct color_t
//程序入口
int main() {
EA::InitGraph(1000,800,"此文件涉嫌违规问题.gif");
//setbkcolor(RED);
EA::DrawTrangle(Point2D(0,0,0,0),Point2D(0,EA::H,0,EA::tH),Point2D(EA::W,0,EA::tW,0));
delay_fps(60);
getch();
closegraph();
return 0;
}
Point2D EA::VerLerp(Point2D c1,Point2D c2,float v) {
return Point2D((c1.xy.x-c2.xy.x)*v+c2.xy.x,(c1.xy.y-c2.xy.y)*v+c2.xy.y,(c1.uv.x-c2.uv.x)*v+c2.uv.x,(c1.uv.y-c2.uv.y)*v+c2.uv.y);
}
void EA::PutPixel_f(Point2D v) {
if(EA::IsCovered(0,W,int(v.xy.x))&&EA::IsCovered(0,H,int(v.xy.y))) {
//☆☆☆这一点很坑,如果写成wBuffer坐标一样的话会出大乱子...引以为戒☆☆☆
wBuffer[int(v.xy.x+v.xy.y*W)]=tBuffer[int(v.uv.x)+int(v.uv.y)*tW];
}
}
void EA::DrawLine_f(float x1,float x2,float y,float u1,float u2,float v) {
if(x1>x2) swap(x1,x2);
Point2D pt;
float k=(u1-u2)/(x1-x2);
for(float i=x1; i<=x2; ++i) {
PutPixel_f(Point2D(i,y,k*(i-x1)+u1,v));
}
}
//v1.y==v2.y
void EA::DrawSimpleTrangle(Point2D v1,Point2D v2,Point2D v3) {
//Format
if(v2.xy.y==v3.xy.y&&v1.xy.y!=v2.xy.y) {
swap(v1,v2);
swap(v2,v3);
} else if(v1.xy.y==v3.xy.y&&v1.xy.y!=v2.xy.y) {
swap(v2,v3);
}
if(v1.xy.x>v2.xy.x) {
swap(v1,v2);
}
//解一次方程
float h=v3.xy.y-v1.xy.y;
float kL=(v3.xy.x-v1.xy.x)/h;
float kR=(v3.xy.x-v2.xy.x)/h;
float xL=0;
float xR=0;
xL=v1.xy.x;
xR=v2.xy.x;
Point2D p1,p2;
if(h>0) {
for(float i=v1.xy.y; i<=v3.xy.y; ++i) {
float percent=1.0f-(i-v1.xy.y)/(v3.xy.y-v1.xy.y);
p1=EA::VerLerp(v1,v3,percent);
p2=EA::VerLerp(v2,v3,percent);
EA::DrawLine_f(xL,xR,i,p1.uv.x,p2.uv.x,p1.uv.y);
xL+=kL;
xR+=kR;
}
} else {
for(float i=v1.xy.y; i>=v3.xy.y; --i) {
float percent=(i-v3.xy.y)/(v1.xy.y-v3.xy.y);
p1=EA::VerLerp(v1,v3,percent);
p2=EA::VerLerp(v2,v3,percent);
EA::DrawLine_f(xL,xR,i,p1.uv.x,p2.uv.x,p1.uv.y);
xL-=kL;
xR-=kR;
}
}
}
void EA::DrawTrangle(Point2D v1,Point2D v2,Point2D v3) {
if(v1.xy.y==v2.xy.y||v1.xy.y==v3.xy.y||v2.xy.y==v3.xy.y) {
EA::DrawSimpleTrangle(v1,v2,v3);
} else {
//找出y中间的顶点,并放在v3的位置
if(v2.xy.y<v1.xy.y&&v1.xy.y<v3.xy.y) {
swap(v1,v3);
} else if(v1.xy.y<v2.xy.y&&v2.xy.y<v3.xy.y) {
swap(v2,v3);
}
//切成两半
float h=v1.xy.y-v2.xy.y;
float w=v1.xy.x-v2.xy.x;
Point2D pt=EA::VerLerp(v2,v1,(v3.xy.y-v2.xy.y)/h);
Point2D vm((w/h)*(v3.xy.y-v2.xy.y)+v2.xy.x,v3.xy.y,pt.uv.x,pt.uv.y);
//画他
DrawSimpleTrangle(v1,vm,v3);
DrawSimpleTrangle(v2,vm,v3);
}
}
#undef ct
项目:EGE纹理映射