shader.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. #ifndef SHADER_H
  2. #define SHADER_H
  3. // ===============================
  4. // AUTHOR : Angel Ortiz (angelo12 AT vt DOT edu)
  5. // CREATE DATE : 2018-07-12
  6. // PURPOSE : Emulate modern programmable vertex and fragment shaders. Allow texture
  7. // reading and full Physically based rendering models.
  8. // ===============================
  9. // SPECIAL NOTES: I kpet the older shaders that I wrote while working towards
  10. // building the final PBR model because I thought it would be nice to see the progression
  11. // Although using pure interface classes would seem to incur a perforamnce
  12. // penalty through pointer chasing I did not measure it through profiling.
  13. // ===============================
  14. //Headers
  15. #include "vector3D.h"
  16. #include "matrix.h"
  17. #include "texture.h"
  18. #define _USE_MATH_DEFINES
  19. #include <math.h>
  20. //Shader Interface for a class that emulates modern GPU fragment and vertex shaders
  21. struct IShader {
  22. virtual ~IShader() {};
  23. virtual Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,
  24. const Vector3f &textureVals,const Vector3f &tangent,
  25. int index, const Vector3f &light = Vector3f{1,1,1}) = 0;
  26. virtual Vector3f fragment(float u, float v) = 0;
  27. };
  28. //Simplest shader. Calculates light intensity per triangle.
  29. struct FlatShader : public IShader {
  30. Matrix4 MVP, MV;
  31. float varIntensity;
  32. Vector3f rgb{255,255,255};
  33. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,
  34. const Vector3f &textureVals,const Vector3f &tangent,
  35. int index, const Vector3f &light) override
  36. {
  37. varIntensity = std::max(0.0f,normal.dotProduct(light));
  38. return MVP.matMultVec(vertex); //Transforms verts into projected space
  39. }
  40. Vector3f fragment(float u, float v) override{
  41. return rgb*varIntensity;
  42. }
  43. };
  44. //More Complex shader that calculates a per-vertex intensity and interpolates
  45. //through the fragments of the triangle
  46. struct GouraudShader : public IShader {
  47. //Per object data
  48. Matrix4 MVP, MV, V, N;
  49. Vector3f lightColor1{1,1,1}, lightColor2{0,0,1}, lightColor3{1,1,1};
  50. float ambientStrength = 0.05, diffStrength = 0, specularStrength = 0.5, spec = 0;
  51. Vector3f rgb{255,255,255};
  52. //Per vertex data
  53. Vector3f varying_diffuse, varying_specular, reflectDir, viewDir, lightDir, correctNormal;
  54. //Per pixel data
  55. Vector3f ambient, diffuse, specular;
  56. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,
  57. const Vector3f &textureVals,const Vector3f &tangent,
  58. int index, const Vector3f &light) override
  59. {
  60. //Vertex attributes
  61. correctNormal = N.matMultDir(normal).normalized();
  62. lightDir = V.matMultDir(light).normalized();
  63. reflectDir = Vector3f::reflect(-lightDir, correctNormal);
  64. viewDir = MV.matMultVec(vertex).normalized();
  65. //Values to be interpolated
  66. varying_specular.data[index] = std::pow( std::max( -viewDir.dotProduct(reflectDir), 0.0f), 32.0f);
  67. varying_diffuse.data[index] = std::max(0.0f, (correctNormal).dotProduct(-lightDir));
  68. return MVP.matMultVec(vertex);
  69. }
  70. Vector3f fragment(float u, float v) override{
  71. //Interpolating
  72. diffStrength = varying_diffuse.x + u*(varying_diffuse.y - varying_diffuse.x) + v*(varying_diffuse.z - varying_diffuse.x);
  73. spec = varying_specular.x + u*(varying_specular.y - varying_specular.x) + v*(varying_specular.z - varying_specular.x);
  74. //Phong reflection model
  75. ambient = lightColor1 * ambientStrength;
  76. diffuse = lightColor2 * diffStrength;
  77. specular = lightColor3 * (specularStrength * spec);
  78. return (ambient + diffuse + specular) * rgb;
  79. }
  80. };
  81. //Even more complex shader that interpolates normals and calculates intensities per fragment instead
  82. //instead of per vertex.
  83. struct PhongShader : public IShader {
  84. //Per object data
  85. Matrix4 MVP, MV, V, N;
  86. float ambientStrength = 0.05, diffStrength = 0, specularStrength = 0.9, spec = 0;
  87. Vector3f lightColor{0,0.1,1},lightColorSpec{1,1,1};
  88. Vector3f rgb{255,255,255};
  89. //Per vertex data
  90. Vector3f normals[3], viewDir[3];
  91. Vector3f varying_diffuse, varying_specular, reflectDir, lightDir;
  92. //Per pixel data
  93. Vector3f ambient, diffuse, specular, interpNormal, interpViewDir;
  94. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,
  95. const Vector3f &textureVals,const Vector3f &tangent,
  96. int index, const Vector3f &light) override
  97. {
  98. //Vertex attributes
  99. normals[index] = N.matMultDir(normal).normalized();
  100. viewDir[index] = MV.matMultVec(vertex).normalized();
  101. lightDir = V.matMultDir(light).normalized();
  102. return MVP.matMultVec(vertex);
  103. }
  104. Vector3f fragment(float u, float v) override{
  105. //Interpolated stuff
  106. interpNormal = normals[0] + (normals[1] - normals[0])* u + (normals[2] - normals[0]) * v;
  107. interpViewDir = viewDir[0] + (viewDir[1] - viewDir[0])* u + (viewDir[2] - viewDir[0]) * v;
  108. //Ambient
  109. ambient = lightColor * ambientStrength;
  110. //Diffuse
  111. diffStrength = std::max(0.0f, (interpNormal.normalized()).dotProduct(lightDir));
  112. diffuse = lightColor * diffStrength;
  113. //Specular
  114. reflectDir = Vector3f::reflect(-lightDir, interpNormal);
  115. spec = std::pow( std::max( (-interpViewDir.normalized()).dotProduct(reflectDir), 0.0f), 50.0f);
  116. specular = lightColorSpec * (specularStrength * spec);
  117. return (ambient + diffuse + specular) * rgb;
  118. }
  119. };
  120. //Optimized version of Phong shader that uses a half angle instead of individual reflection
  121. //angles
  122. struct BlinnPhongShader : public IShader {
  123. //Per object data
  124. Texture *albedoT;
  125. Matrix4 MVP, MV, V, N;
  126. float ambientStrength = 0.05, diffStrength=1 , specularStrength= 0.5;
  127. Vector3f lightColor{1,1,1};
  128. //Per vertex data
  129. Vector3f normals[3], viewDir[3], UV[3];
  130. float diff, spec, shininess = 128;
  131. //Per fragment data
  132. Vector3f ambient, diffuse, specular, interpNormal, interpViewDir, interpUV;
  133. Vector3f halfwayDir, lightDir;
  134. Vector3f interpCol, white{255,255,255};
  135. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,
  136. const Vector3f &textureVals,const Vector3f &tangent,
  137. int index, const Vector3f &light) override{
  138. normals[index] = N.matMultDir(normal).normalized();
  139. UV[index] = textureVals;
  140. viewDir[index] = MV.matMultVec(vertex).normalized();
  141. lightDir = V.matMultDir(light).normalized();
  142. return MVP.matMultVec(vertex);
  143. }
  144. Vector3f fragment(float u, float v) override{
  145. //Interpolated stuff
  146. interpNormal = (normals[0] + (normals[1] - normals[0])* u + (normals[2] - normals[0]) * v).normalized();
  147. interpViewDir = viewDir[0] + (viewDir[1] - viewDir[0])* u + (viewDir[2] - viewDir[0]) * v;
  148. interpUV = UV[0] + (UV[1] - UV[0])* u + (UV[2] - UV[0]) * v;
  149. //Albedo
  150. interpCol = albedoT->getPixelVal(interpUV.x, interpUV.y);
  151. //Ambient
  152. ambient = lightColor * ambientStrength;
  153. //Diffuse
  154. diff = std::max(0.0f, interpNormal.dotProduct(lightDir));
  155. diffuse = lightColor * diff * diffStrength;
  156. //Specular
  157. halfwayDir = (lightDir - interpViewDir).normalized();
  158. spec = std::pow(std::max(0.0f, interpNormal.dotProduct(halfwayDir)), shininess);
  159. specular = lightColor * spec * specularStrength;
  160. return (ambient + diffuse) * interpCol + specular * white;
  161. }
  162. };
  163. // Shader that uses texture mapping extensively
  164. struct TextureMapShader : public IShader {
  165. //Variables set per model
  166. Texture *albedoT, *normalT, *ambientOT;
  167. Matrix4 MVP, MV, V, M, N;
  168. Vector3f cameraPos;
  169. //Light Variables
  170. Vector3f lightColor{1,1,1}, white{1,1,1};
  171. float ambientStrength = 0.1, diffStrength = 0.9, specularStrength = 0.8;
  172. float diff, spec, shininess = 128;
  173. Vector3f lightDir[3];
  174. //Variables set per vertex
  175. Vector3f viewDir[3], texCoords[3];
  176. Vector3f normal_WS, tangent_WS, biTangent_WS;
  177. Matrix4 TBN;
  178. //Interpolated variables
  179. Vector3f interpCoords, interpLightDir, interpNormal,
  180. interpViewDir, interpCol, interpAO;
  181. //Per fragment
  182. Vector3f ambient, diffuse, specular ;
  183. Vector3f halfwayDir;
  184. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,
  185. const Vector3f &textureVals, const Vector3f &tangent,
  186. int index, const Vector3f &light) override{
  187. //Creating TBN matrix
  188. normal_WS = N.matMultDir(normal).normalized();
  189. tangent_WS = N.matMultDir(tangent).normalized();
  190. biTangent_WS = normal_WS.crossProduct(tangent_WS);
  191. TBN = Matrix4::TBNMatrix(tangent_WS, biTangent_WS, normal_WS);
  192. //Getting UV coordinates for use in both albedo and normal textures
  193. texCoords[index] = textureVals;
  194. //Passing all lighting related data to tangent space
  195. lightDir[index] = TBN.matMultVec(light);
  196. viewDir[index] = TBN.matMultVec(cameraPos - M.matMultVec(vertex));
  197. return MVP.matMultVec(vertex);
  198. }
  199. Vector3f fragment(float u, float v) override{
  200. //Interpolated attributes
  201. interpCoords = texCoords[0] + (texCoords[1] - texCoords[0])* u + (texCoords[2] - texCoords[0]) * v;
  202. interpLightDir = lightDir[0] + (lightDir[1] - lightDir[0])* u + (lightDir[2] - lightDir[0]) * v;
  203. interpViewDir = viewDir[0] + (viewDir[1] - viewDir[0])* u + (viewDir[2] - viewDir[0]) * v;
  204. //Reading albedo and normal data from textures
  205. interpCol = albedoT->getPixelVal(interpCoords.x, interpCoords.y);
  206. interpAO = ambientOT->getIntensityVal(interpCoords.x, interpCoords.y);
  207. interpNormal = normalT->getPixelVal(interpCoords.x, interpCoords.y);
  208. interpNormal = interpNormal.normalized();
  209. //Ambient
  210. ambient = lightColor * ambientStrength * interpAO;
  211. //Diffuse
  212. diff = std::max(0.0f, interpNormal.dotProduct(interpLightDir));
  213. diffuse = lightColor * diff * diffStrength;
  214. //Specular
  215. halfwayDir = (interpLightDir + interpViewDir).normalized();
  216. spec = std::pow(std::max(0.0f, interpNormal.dotProduct(halfwayDir)), shininess);
  217. specular = lightColor * spec * specularStrength;
  218. return (ambient + diffuse) * interpCol + specular * white;
  219. }
  220. };
  221. // Shader that uses texture mapping and a PBR approach for shading
  222. // Uses a cook-torrance BRDF for direct light sources.
  223. struct PBRShader : public IShader {
  224. //Variables set per model
  225. Texture *albedoT, *normalT, *ambientOT, *roughT, *metalT;
  226. Matrix4 MVP, MV, V, M, N;
  227. Vector3f cameraPos;
  228. //Light Variables
  229. Vector3f F0{0.04, 0.04, 0.04}, F0corrected; //Default value dielectric
  230. Vector3f *lightDirVal, *lightCol, *lightPos;
  231. float nDotL, nDotV, ambientInt = 0.01;
  232. int numLights;
  233. //Variables set per vertex
  234. Vector3f viewDir[3], texCoords[3];
  235. Vector3f normal_WS, tangent_WS, biTangent_WS;
  236. Matrix4 TBN;
  237. //Interpolated variables
  238. Vector3f interpCoords, interpNormal, interpViewDir, interpCol;
  239. //Per fragment
  240. Vector3f radianceOut, ambient;
  241. float interpRough, interpAO, interpMetal;
  242. float uTexture, vTexture, intPart;
  243. //BRDF functions
  244. Vector3f fresnelSchlick(float cosTheta, Vector3f &fresnel0 ){
  245. float invcCosTheta = 1.0 - cosTheta;
  246. return fresnel0 + (Vector3f(1.0)- fresnel0) * (invcCosTheta * invcCosTheta * invcCosTheta * invcCosTheta * invcCosTheta);
  247. }
  248. float distributionGGX(Vector3f normal, Vector3f halfway, float roughness){
  249. float a = roughness*roughness;
  250. float a2 = a*a;
  251. float NdotH = std::max(normal.dotProduct(halfway), 0.0f);
  252. float NdotH2 = NdotH*NdotH;
  253. float denom = (NdotH2 * (a2 - 1.0f) + 1.0f);
  254. denom = 1.0f / (M_PI * denom * denom);
  255. return a2 * denom;
  256. }
  257. float GeometrySchlickGGX(float Ndot, float roughness){
  258. float r = (roughness + 1.0f);
  259. float k = (r*r) / 8.0f; //Only useful for direct lighting must be changed in ibr
  260. float denom = 1.0f / (Ndot * (1.0f- k) + k);
  261. return Ndot * denom;
  262. }
  263. float GeometrySmith(float roughness, float nDL, float nDV){
  264. return GeometrySchlickGGX(nDL, roughness) * GeometrySchlickGGX(nDV, roughness);
  265. }
  266. //Vertex shader
  267. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,
  268. const Vector3f &textureVals, const Vector3f &tangent,
  269. int index, const Vector3f &light = Vector3f{1,1,1}) override
  270. {
  271. //Creating TBN matrix
  272. normal_WS = N.matMultDir(normal).normalized();
  273. tangent_WS = N.matMultDir(tangent).normalized();
  274. biTangent_WS = normal_WS.crossProduct(tangent_WS);
  275. TBN = Matrix4::TBNMatrix(tangent_WS, biTangent_WS, normal_WS);
  276. //Getting UV coordinates for use in all textures
  277. texCoords[index] = textureVals;
  278. //Passing all lighting related data to tangent space
  279. for(int lIndex = 0; lIndex < numLights; ++lIndex){
  280. int indc2 = (lIndex*3) + index;
  281. lightDirVal[indc2] = TBN.matMultDir(lightPos[lIndex]);
  282. }
  283. viewDir[index] = TBN.matMultDir(cameraPos - M.matMultVec(vertex));
  284. return MVP.matMultVec(vertex);
  285. }
  286. //Fragment shader
  287. Vector3f fragment(float u, float v) override{
  288. //Interpolated attributes
  289. interpCoords = texCoords[0] + (texCoords[1] - texCoords[0])* u + (texCoords[2] - texCoords[0]) * v;
  290. interpViewDir = viewDir[0] + (viewDir[1] - viewDir[0])* u + (viewDir[2] - viewDir[0]) * v;
  291. //Correcting UV's for tiling
  292. uTexture = std::modf(interpCoords.x, &intPart);
  293. vTexture = std::modf(interpCoords.y, &intPart);
  294. //Reading data from textures for use in lighting calculations
  295. interpCol = albedoT->getPixelVal(uTexture, vTexture);
  296. interpAO = ambientOT->getIntensityVal(uTexture, vTexture);
  297. interpRough = roughT->getIntensityVal(uTexture, vTexture);;
  298. interpMetal = metalT->getIntensityVal(uTexture, vTexture);
  299. interpNormal = normalT->getPixelVal(uTexture, vTexture);
  300. interpNormal = interpNormal.normalized();
  301. interpViewDir = interpViewDir.normalized();
  302. //Varying f0 based on metallicness of surface
  303. float invMetal = (1.0f-interpMetal);
  304. F0corrected = (F0 * invMetal) + (interpCol * interpMetal);
  305. nDotV = std::max(interpNormal.dotProduct(interpViewDir), 0.0f);
  306. //Setting up Direct Lighting variables
  307. const int maxLights = numLights;
  308. //Fresnel, normal distribution function and geometry occlusion
  309. Vector3f *F = new Vector3f[maxLights];
  310. float *NDF = new float[maxLights];
  311. float *G = new float[maxLights];
  312. //Storing in array for vectorizing
  313. Vector3f *radianceLights = new Vector3f[maxLights];
  314. Vector3f *interpLightDir = new Vector3f[maxLights];
  315. Vector3f *halfwayDir = new Vector3f[maxLights];
  316. Vector3f *numerator = new Vector3f[maxLights];
  317. Vector3f *specular = new Vector3f[maxLights];
  318. Vector3f *kD = new Vector3f[maxLights];
  319. float *nDotL = new float[maxLights];
  320. float *invDenominator = new float[maxLights];
  321. //Interpolating each light direction for every light
  322. int val;
  323. for(int i = 0; i < maxLights; ++i ){
  324. val = i*3;
  325. interpLightDir[i] = (lightDirVal[val] + (lightDirVal[val + 1] - lightDirVal[val])* u + (lightDirVal[val + 2] - lightDirVal[val]) * v).normalized();
  326. }
  327. //Per light illumination calculations that can be simdified
  328. //Currently uses widest SIMD array to perform all light iterations in one trip
  329. //Which I believe leaves some extra
  330. for(int light = 0; light < maxLights; ++light ){
  331. halfwayDir[light] = (interpLightDir[light] + interpViewDir);
  332. halfwayDir[light] = halfwayDir[light].normalized();
  333. nDotL[light] = std::fmax(interpNormal.dotProduct(interpLightDir[light]), 0.0f);
  334. //No problem vectorizing these functions because they are inlined by the compiler
  335. //And also only consist of math operations to the vectors
  336. F[light] = fresnelSchlick(std::fmax(halfwayDir[light].dotProduct(interpViewDir), 0.0f), F0corrected);
  337. NDF[light] = distributionGGX(interpNormal, halfwayDir[light], interpRough);
  338. G[light] = GeometrySmith(interpRough, nDotL[light] , nDotV);
  339. numerator[light] = F[light] * G[light]*NDF[light];
  340. invDenominator[light] = 1.0f / std::fmax(4.0f * (nDotL[light] * nDotV), 0.001f);
  341. specular[light] = numerator[light] * invDenominator[light];
  342. //kd is 1 - kf which is the stuff to the right of the vecotr
  343. kD[light] = (Vector3f(1.0f) - F[light])*invMetal;
  344. //The rendering equation result for a given light
  345. radianceLights[light] = (kD[light] * (interpCol * (1.0f / M_PI) + specular[light] )) * nDotL[light] * lightCol[light];
  346. }
  347. //Summing up all radiance values since SIMD won't work if I do this within the
  348. //previous loop
  349. radianceOut.zero();
  350. for(int i = 0; i < maxLights; ++i) {
  351. radianceOut += radianceLights[i];
  352. }
  353. //Simplistic ambient term
  354. ambient = interpCol * (ambientInt * interpAO);
  355. //Deleting dynamically allocated arrays
  356. delete radianceLights;
  357. delete interpLightDir;
  358. delete halfwayDir;
  359. delete numerator;
  360. delete specular;
  361. delete kD;
  362. delete F;
  363. delete NDF;
  364. delete G;
  365. delete nDotL;
  366. delete invDenominator;
  367. return ambient + radianceOut;
  368. }
  369. };
  370. #endif