rasterizer.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // ===============================
  2. // AUTHOR : Angel Ortiz (angelo12 AT vt DOT edu)
  3. // CREATE DATE : 2018-07-03
  4. // ===============================
  5. //Includes
  6. #include "rasterizer.h"
  7. #include <algorithm>
  8. //Gamma correction lookup table, much much faster than actually calculating it
  9. const int Rasterizer::gammaTable[256] = {0, 21, 28, 34, 39, 43, 46,
  10. 50, 53, 56, 59, 61, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84,
  11. 85, 87, 89, 90, 92, 93, 95, 96, 98, 99, 101, 102, 103, 105, 106,
  12. 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 119, 120, 122,
  13. 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
  14. 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147,
  15. 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 156, 157, 158,
  16. 159, 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168,
  17. 169, 170, 170, 171, 172, 173, 173, 174, 175, 175, 176, 177, 178,
  18. 178, 179, 180, 180, 181, 182, 182, 183, 184, 184, 185, 186, 186,
  19. 187, 188, 188, 189, 190, 190, 191, 192, 192, 193, 194, 194, 195,
  20. 195, 196, 197, 197, 198, 199, 199, 200, 200, 201, 202, 202, 203,
  21. 203, 204, 205, 205, 206, 206, 207, 207, 208, 209, 209, 210, 210,
  22. 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218,
  23. 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, 224, 225,
  24. 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231,
  25. 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238,
  26. 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244,
  27. 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 249, 250, 250,
  28. 251, 251, 252, 252, 253, 253, 254, 254, 255, 255};
  29. //Initializing all the basic colors
  30. const SDL_PixelFormat* Rasterizer::mappingFormat( SDL_AllocFormat(PIXEL_FORMAT));
  31. const Uint32 Rasterizer::white(SDL_MapRGBA(mappingFormat, 0xFF,0xFF,0xFF,0xFF));
  32. const Uint32 Rasterizer::red(SDL_MapRGBA(mappingFormat, 0xFF,0x00,0x00,0xFF));
  33. const Uint32 Rasterizer::green(SDL_MapRGBA(mappingFormat, 0x00,0xFF,0x00,0xFF));
  34. const Uint32 Rasterizer::blue(SDL_MapRGBA(mappingFormat, 0x00,0x00,0xFF,0xFF));
  35. //Early pixel buffer traversal test
  36. void Rasterizer::makeCoolPattern(Buffer<Uint32> *pixelBuffer){
  37. for(int y = 0; y < pixelBuffer->mHeight; ++y){
  38. for(int x = 0; x < pixelBuffer->mWidth; ++x){
  39. Uint8 r = x*2 % 256;
  40. Uint8 g = y/8 % 256;
  41. Uint8 b = r*y % 256;
  42. Uint32 color = SDL_MapRGBA(mappingFormat, r,g,b,0xFF);
  43. (*pixelBuffer)(x,y) = color;
  44. }
  45. }
  46. }
  47. void Rasterizer::testPattern(Buffer<Uint32> *pixelBuffer){
  48. (*pixelBuffer)(600,200) = red;
  49. (*pixelBuffer)(400,200) = green;
  50. (*pixelBuffer)(200,200) = blue;
  51. }
  52. void Rasterizer::drawLine(Vector3f &vertex1, Vector3f &vertex2,const Uint32 &color, Buffer<Uint32> *pixelBuffer ){
  53. //NDC to viewport transform
  54. int x1 = (vertex1.x + 1 ) * pixelBuffer->mWidth * 0.5;
  55. int y1 = (-vertex1.y + 1 ) * pixelBuffer->mHeight * 0.5;
  56. int x2 = (vertex2.x +1 ) * pixelBuffer->mWidth * 0.5;
  57. int y2 = (-vertex2.y +1 ) * pixelBuffer->mHeight * 0.5;
  58. //transpose line if it is too steep
  59. bool steep = false;
  60. if (std::abs(x1-x2) < std::abs(y1-y2) ){
  61. std::swap(x1,y1);
  62. std::swap(x2,y2);
  63. steep = true;
  64. }
  65. //Redefine line so that it is left to right
  66. if ( x1 > x2 ){
  67. std::swap(x1,x2);
  68. std::swap(y1,y2);
  69. }
  70. //Redefined to use only int arithmetic
  71. int dx = x2 - x1;
  72. int dy = y2 - y1;
  73. int derror2 = std::abs(dy)*2;
  74. int error2 = 0;
  75. int y = y1;
  76. for(int x=x1; x <= x2 ; x++){
  77. if(steep){
  78. (*pixelBuffer)(y, x) = color; //Swap back because of steep transpose
  79. }
  80. else{
  81. (*pixelBuffer)(x,y) = color;
  82. }
  83. error2 += derror2;
  84. if (error2 > dx){
  85. y += (y2 > y1 ? 1 : -1);
  86. error2 -= dx*2;
  87. }
  88. }
  89. }
  90. void Rasterizer::drawWireFrame(Vector3f *vertices, IShader &shader, Buffer<Uint32> *pixelBuffer){
  91. drawLine(vertices[0], vertices[1], red, pixelBuffer);
  92. drawLine(vertices[1], vertices[2], green, pixelBuffer);
  93. drawLine(vertices[0], vertices[2], blue, pixelBuffer);
  94. }
  95. //Candidate for future total overhaul into a SIMD function
  96. //Why not now? Before doing this I have to do:
  97. //1.vectorize: Shader, vector3 class rewrite
  98. //2.Edge detection
  99. //3.Fixed point rewrite
  100. //4.SDL function rewrite
  101. //5.Zbuffer rewrite
  102. void Rasterizer::drawTriangles(Vector3f *vertices, IShader &shader, Buffer<Uint32> *pixelBuffer, Buffer<float> *zBuffer){
  103. //Per triangle variables
  104. float area;
  105. Vector3f hW{1/vertices[0].w, 1/vertices[1].w, 1/vertices[2].w};
  106. //Per fragment variables
  107. float depth, uPers, vPers, areaPers, count = 0; //u, v, are perspective corrected
  108. Vector3f e, e_row, f;
  109. Vector3f rgbVals{255,255,255};
  110. //Transform into viewport coordinates
  111. Rasterizer::viewportTransform(pixelBuffer, vertices);
  112. area = edge(vertices[0],vertices[1],vertices[2]);
  113. if(area <= 0 ) return;
  114. area = 1/area;
  115. //Finding triangle bounding box limits & clips it to the screen width and height
  116. int xMax, xMin, yMax, yMin;
  117. Rasterizer::triBoundBox(xMax, xMin, yMax, yMin, vertices, pixelBuffer);
  118. //Per triangle variables
  119. Vector3f zVals{vertices[0].z,vertices[1].z,vertices[2].z};
  120. float A01 = vertices[0].y - vertices[1].y, B01= vertices[1].x - vertices[0].x;
  121. float A12 = vertices[1].y - vertices[2].y, B12= vertices[2].x - vertices[1].x;
  122. float A20 = vertices[2].y - vertices[0].y, B20= vertices[0].x - vertices[2].x;
  123. //Edge values at the first corner of the bounding box
  124. Vector3f point{(float)xMin, (float)yMin, 0};
  125. e_row.x = edge(vertices[1], vertices[2], point);
  126. e_row.y = edge(vertices[2], vertices[0], point);
  127. e_row.z = edge(vertices[0], vertices[1], point);
  128. //Iterating through each pixel in triangle bounding box
  129. for(int y = yMin; y <= yMax; ++y){
  130. //Bary coordinates at start of row
  131. e.x = e_row.x;
  132. e.y = e_row.y;
  133. e.z = e_row.z;
  134. for(int x = xMin; x <= xMax; ++x){
  135. //Originally I followed top left rule to avoid edge rewrites but it was
  136. //costing me more to perform this check every pixel than just allowing the rewrite
  137. //It's saved in case the pixel shader gets more complex and the tradeoff becomes
  138. //worth it
  139. // if(inside(e.x, A01, B01) && inside(e.y, A12, B12) && inside(e.z, A20, B20) ){
  140. //Only draw if pixel inside triangle
  141. if( (e.x >= 0) && (e.y >= 0 ) && (e.z >= 0 )){
  142. //Zbuffer check
  143. depth = (e*area).dotProduct(zVals);
  144. if((*zBuffer)(x,y) < depth && depth <= 1.0){
  145. (*zBuffer)(x,y) = depth;
  146. //Get perspective correct barycentric coords
  147. f = e * hW;
  148. areaPers = 1 / (f.data[0] + f.data[1] + f.data[2]);
  149. uPers = f.data[1] * areaPers;
  150. vPers = f.data[2] * areaPers;
  151. //Run fragment shader (U, v are barycentric coords)
  152. rgbVals = shader.fragment(uPers , vPers);
  153. //Update pixel buffer with clamped values
  154. (*pixelBuffer)(x,y) = SDL_MapRGB(mappingFormat,
  155. gammaAdjust(rgbVals.data[0]),
  156. gammaAdjust(rgbVals.data[1]),
  157. gammaAdjust(rgbVals.data[2]));
  158. }
  159. }
  160. //One step to the right
  161. e.x += A12;
  162. e.y += A20;
  163. e.z += A01;
  164. }
  165. //One row step
  166. e_row.x += B12;
  167. e_row.y += B20;
  168. e_row.z += B01;
  169. }
  170. }
  171. void Rasterizer::viewportTransform(Buffer<Uint32> *pixelBuffer, Vector3f *vertices){
  172. for(int i = 0; i < 3; ++i){
  173. //Adding half a pixel to avoid gaps on small vertex values
  174. vertices[i].x = ((vertices[i].x + 1 ) * pixelBuffer->mWidth * 0.5) + 0.5;
  175. vertices[i].y = ((vertices[i].y + 1 ) * pixelBuffer->mHeight * 0.5) + 0.5;
  176. }
  177. }
  178. void Rasterizer::triBoundBox(int &xMax, int &xMin, int &yMax, int &yMin,Vector3f *vertices, Buffer<Uint32> *pixelBuffer){
  179. // xMax = std::ceil(std::max({vertices[0].x, vertices[1].x, vertices[2].x}));
  180. // xMin = std::ceil(std::min({vertices[0].x, vertices[1].x, vertices[2].x}));
  181. xMax = std::max({vertices[0].x, vertices[1].x, vertices[2].x});
  182. xMin = std::min({vertices[0].x, vertices[1].x, vertices[2].x});
  183. // yMax = std::ceil(std::max({vertices[0].y, vertices[1].y, vertices[2].y}));
  184. // yMin = std::ceil(std::min({vertices[0].y, vertices[1].y, vertices[2].y}));
  185. yMax = std::max({vertices[0].y, vertices[1].y, vertices[2].y});
  186. yMin = std::min({vertices[0].y, vertices[1].y, vertices[2].y});
  187. //Clip against screen
  188. xMax = std::min(xMax, pixelBuffer->mWidth -1);
  189. xMin = std::max(xMin, 0);
  190. yMax = std::min(yMax, pixelBuffer->mHeight -1);
  191. yMin = std::max(yMin, 0);
  192. }
  193. float Rasterizer::edge(Vector3f &a, Vector3f &b, Vector3f &c){
  194. return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
  195. }
  196. //Doing this check was slower than allowing overwrites on certain triangle edges so it's
  197. bool Rasterizer::inside(float e, float a, float b){
  198. if ( e > 0 ) return true;
  199. if ( e < 0 ) return false;
  200. if ( a > 0 ) return true;
  201. if ( a < 0 ) return false;
  202. if ( b > 0 ) return true;
  203. return false;
  204. }
  205. float Rasterizer::clamp(float n, float lower, float upper) {
  206. return std::max(lower, std::min(n, upper));
  207. }
  208. //Gamma adjustment table precalculated for a 2.2 gamma value
  209. //signficant ms gains from this!!
  210. int Rasterizer::gammaAdjust(float n) {
  211. int val = round(clamp(n*255, 0, 255));
  212. return gammaTable[val];
  213. }