Last active
January 19, 2020 02:19
-
-
Save Steve132/dd30fdfcade220c0b9da to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<iostream> | |
#include<Eigen/Dense> | |
#include<Eigen/LU> | |
#include<vector> | |
using namespace std; | |
template<class PixelType> | |
class Framebuffer | |
{ | |
protected: | |
std::vector<PixelType> data; | |
size_t fw,fh; | |
public: | |
const size_t& width; | |
const size_t& height; | |
Framebuffer(size_t w,size_t h): | |
data(w*h), | |
fw(w),fh(h), | |
width(fw),height(fh) | |
{} | |
PixelType& operator()(size_t x,size_t y) | |
{ | |
return data[y*width+x]; | |
} | |
const PixelType& operator()(size_t x,size_t y) const | |
{ | |
return data[y*width+x]; | |
} | |
}; | |
template<class VertexVsIn,class VertexVsOut> | |
void run_vertex_shader(const VertexVsIn* b,const VertexVsIn* e,VertexVsOut* o, | |
const std::function<VertexVsOut (const VertexVsIn&)>& vertex_shader) | |
{ | |
size_t n=e-b; | |
#pragma omp parallel for | |
for(size_t i=0;i<n;i++) | |
{ | |
o[i]=vertex_shader(b[i]); | |
} | |
} | |
template<class PixelOut,class VertexVsOut> | |
void rasterize_triangle(Framebuffer<PixelOut>& fb,const std::array<VertexVsOut,3>& verts,const std::function<PixelOut (const VertexVsOut&)>& fragment_shader) | |
{ | |
std::array<Eigen::Vector4f,3> points{{verts[0].position(),verts[1].position(),verts[2].position()}}; | |
std::array<Eigen::Vector3f,3> epoints{{points[0]/points[0][3],points[1]/points[1][3],points[2]/points[2][3]}}; | |
//calculate the bounding box of the triangle in screen space floating point. | |
Eigen::Vector2f bb_ul=epoints[0].head<2>().min(epoints[1].head<2>()).min(epoints[2].head<2>()); | |
Eigen::Vector2f bb_lr=epoints[0].head<2>().max(epoints[1].head<2>()).max(epoints[2].head<2>()); | |
Eigen::Vector2ui isz(fb.width,fb.height); | |
Eigen::Vector2ui ibb_ul=bb_ul.array()*isz,ibb_lr=bb_lr.array()*isz; | |
//clamp the bounding box to the framebuffer size | |
ibb_ul=ibb_ul.max(Eigen::Vector2ui(0,0)); | |
ibb_lr=ibb_lr.min(isz); | |
//Pre-compute barycentric | |
Eigen::Matrix2f T; | |
T << (epoints[0].head<2>()-epoints[2].head<2>()),(epoints[1].head<2>()-epoints[2].head<2>()); | |
Eigen::Matrix2f Ti=T.inverse(); | |
for(int y=ibb_ul[1];y<ibb_lr[1];y++) | |
for(int x=ibb_ul[0];x<ibb_lr[0];x++) | |
{ | |
Eigen::Vector2f ssc(x,y); | |
//Compute barycentric coordinates | |
Eigen::Vector3f bary; | |
bary.head<2>()=Ti*(ssc-epoints[2].head<2>()); | |
bary[3]=1.0f-bary[0]-bary[1]; | |
if((bary.array() < 1.0f).all() && (bary.array() > 0.0f).all()) | |
{ | |
float d=bary[0]*epoints[0][2]+bary[1]*epoints[1][2]+bary[2]*epoints[2][2]; | |
PixelOut& po=fb(x,y); | |
if(po.depth() < d) //depth test | |
{ | |
VertexVsOut v; | |
for(int i=0;i<3;i++) | |
{ | |
VertexVsOut vt=verts[i]; | |
vt*=bary[i]; | |
v+=vt; //interpolate varying parameters | |
} | |
po=fragment_shader(v); | |
po.depth()=d; | |
} | |
} | |
} | |
} | |
template<class PixelOut,class VertexVsOut> | |
void rasterize(Framebuffer<PixelOut>& fb,const size_t* ib,const size_t* ie,const VertexVsOut* verts, | |
const std::function<PixelOut (const VertexVsOut&)>& fragment_shader) | |
{ | |
size_t n=ie-ib; | |
#pragma omp parallel for | |
for(size_t i=0;i<n;i+=3) | |
{ | |
const size_t* ti=ib+i; | |
rasterize_triangle(fb,{{verts[ti[0]],verts[ti[1]],verts[ti[2]]}},fragment_shader); | |
} | |
} | |
template<class PixelOut,class VertexVsOut,class VertexVsIn> | |
void draw( Framebuffer<PixelOut>& fb, | |
const VertexVsIn* vertexbuffer_b,const VertexVsIn* vertexbuffer_e, | |
const size_t* indexbuffer_b,const size_t* indexbuffer_e, | |
VertexVsOut* vcache_b,VertexVsOut* vcache_e, | |
const std::function<VertexVsOut (const VertexVsIn&)>& vertex_shader, | |
const std::function<PixelOut (const VertexVsOut&)>& fragment_shader) | |
{ | |
std::unique_ptr<VertexVsOut[]> vc; | |
if(vcache_b==NULL || (vcache_e-vcache_b) != (vertexbuffer_e-vertexbuffer_b)) | |
{ | |
vcache_b=new VertexVsOut[(vertexbuffer_e-vertexbuffer_b)]; | |
vc.reset(vcache_b); | |
} | |
run_vertex_shader(vertexbuffer_b,vertexbuffer_e,vcache_b,vertex_shader); | |
rasterize(fb,indexbuffer_b,indexbuffer_e,vcache_b,fragment_shader); | |
} | |
///EXAMPLE USAGE | |
struct TeapotVert | |
{ | |
Eigen::Vertex3f p; | |
}; | |
struct TeapotVertVsOut | |
{ | |
Eigen::Vertex4f p; | |
Eigen::Vertex3f color; | |
const Eigen::Vertex4f& position() const | |
{ | |
return p; | |
} | |
TeapotVertVsOut& operator+=(const TeapotVertVsOut& tp) | |
{ | |
p+=tp.p; | |
color+=tp.color; | |
return *this; | |
} | |
TeapotVertVsOut& operator*=(const float& f) | |
{ | |
p*=f;color*=f;return *this; | |
} | |
}; | |
union TeapotPixel | |
{ | |
Eigen::Vertex4f color; | |
struct {float r,g,b,d;}; | |
float& depth() { return d; } | |
}; | |
TeapotVertVsOut example_vertex_shader(const TeapotVert& vin,const Matrix4f& mvp,float t) | |
{ | |
TeapotVertVsOut vout; | |
vout.p.head<3>()=mvp*vin; | |
vout.p[3]=1.0f; | |
vout.color=Eigen::Vector3f(0.0,1.0f,sin(t)); | |
return vout; | |
} | |
PixelOut example_fragment_shader(const TeapotVertVsOut& fsin) | |
{ | |
PixelOut p; | |
p.color.head<3>()=fsin.color; | |
return p; | |
} | |
Fragmentshader<TeapotPixel> tp; | |
draw(tp,vb,vbe,ib,ibe,NULL,NULL, | |
bind(example_vertex_shader,_1,camera_matrix,time), | |
example_fragment_shader | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment