Skip to content

Instantly share code, notes, and snippets.

@Steve132
Last active January 19, 2020 02:19
Show Gist options
  • Save Steve132/dd30fdfcade220c0b9da to your computer and use it in GitHub Desktop.
Save Steve132/dd30fdfcade220c0b9da to your computer and use it in GitHub Desktop.
#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