shader.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. #ifndef SHADER_H
  2. #define SHADER_H
  3. #include "vector3D.h"
  4. #include "matrix.h"
  5. #include "texture.h"
  6. #include <array>
  7. //Shader Interface for a class that emulates modern GPU fragment and vertex shaders
  8. struct IShader {
  9. virtual ~IShader() {};
  10. virtual Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,const Vector3f &textureVals,const Vector3f &tangent, const Vector3f &light, int index) = 0;
  11. virtual Vector3f fragment(float u, float v) = 0;
  12. };
  13. //Simplest shader. Calculates light intensity per triangle.
  14. struct FlatShader : public IShader {
  15. Matrix4 MVP, MV;
  16. float varIntensity;
  17. Vector3f rgb{255,255,255};
  18. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,const Vector3f &textureVals,const Vector3f &tangent, const Vector3f &light, int index) override{
  19. varIntensity = std::max(0.0f,normal.dotProduct(light));
  20. return MVP.matMultVec(vertex); //Transforms verts into projected space
  21. }
  22. Vector3f fragment(float u, float v) override{
  23. return rgb*varIntensity;
  24. }
  25. };
  26. //More Complex shader that calculates a per-vertex intensity and interpolates
  27. //through the fragments of the triangle
  28. struct GouraudShader : public IShader {
  29. Matrix4 MVP, MV, V, N;
  30. float ambientStrength = 0.05, diffStrength = 0, specularStrength = 0.5, spec = 0;
  31. Vector3f ambient, diffuse, specular;
  32. Vector3f lightColor1{1,1,1}, lightColor2{0,0,1}, lightColor3{1,1,1};
  33. Vector3f varying_diffuse, varying_specular, reflectDir, viewDir, light2, normal2;
  34. Vector3f rgb{255,255,255};
  35. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,const Vector3f &textureVals,const Vector3f &tangent, const Vector3f &light, int index) override{
  36. normal2 = N.matMultDir(normal).normalized();
  37. light2 = V.matMultDir(light).normalized();
  38. reflectDir = Vector3f::reflect(-light2, normal);
  39. viewDir = MV.matMultVec(vertex).normalized();
  40. varying_specular.data[index] = std::pow( std::max( -viewDir.dotProduct(reflectDir), 0.0f), 32.0f);
  41. varying_diffuse.data[index] = std::max(0.0f, (normal).dotProduct(-light2));
  42. return MVP.matMultVec(vertex);
  43. }
  44. Vector3f fragment(float u, float v) override{
  45. ambient = lightColor1 * ambientStrength;
  46. diffStrength = varying_diffuse.x + u*(varying_diffuse.y - varying_diffuse.x) + v*(varying_diffuse.z - varying_diffuse.x);
  47. diffuse = lightColor2 * diffStrength;
  48. spec = varying_specular.x + u*(varying_specular.y - varying_specular.x) + v*(varying_specular.z - varying_specular.x);
  49. specular = lightColor3 * (specularStrength * spec);
  50. return (ambient + diffuse + specular) * rgb;
  51. }
  52. };
  53. //Even more complex shader that interpolates normals and calculates intensities per fragment instead
  54. //instead of per vertex.
  55. struct PhongShader : public IShader {
  56. Matrix4 MVP, MV, V, N;
  57. float ambientStrength = 0.05, diffStrength = 0, specularStrength = 0.9, spec = 0;
  58. Vector3f normals[3], viewDir[3];
  59. Vector3f ambient, diffuse, specular, interpNormal, interpViewDir;
  60. Vector3f lightColor{0,0.1,1},lightColorSpec{1,1,1};
  61. Vector3f varying_diffuse, varying_specular, reflectDir, light2;
  62. Vector3f rgb{255,255,255};
  63. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,const Vector3f &textureVals,const Vector3f &tangent, const Vector3f &light, int index) override{
  64. normals[index] = N.matMultDir(normal).normalized();
  65. viewDir[index] = MV.matMultVec(vertex).normalized();
  66. light2 = V.matMultDir(light).normalized();
  67. return MVP.matMultVec(vertex);
  68. }
  69. Vector3f fragment(float u, float v) override{
  70. //Interpolated stuff
  71. interpNormal = normals[0] + (normals[1] - normals[0])* u + (normals[2] - normals[0]) * v;
  72. interpViewDir = viewDir[0] + (viewDir[1] - viewDir[0])* u + (viewDir[2] - viewDir[0]) * v;
  73. //Ambient
  74. ambient = lightColor * ambientStrength;
  75. //Diffuse
  76. diffStrength = std::max(0.0f, (interpNormal.normalized()).dotProduct(light2));
  77. diffuse = lightColor * diffStrength;
  78. //Specular
  79. reflectDir = Vector3f::reflect(-light2, interpNormal);
  80. spec = std::pow( std::max( (-interpViewDir.normalized()).dotProduct(reflectDir), 0.0f), 50.0f);
  81. specular = lightColorSpec * (specularStrength * spec);
  82. return (ambient + diffuse + specular) * rgb;
  83. }
  84. };
  85. //Optimized version of Phong shader that uses a half angle instead of individual reflection
  86. //angles
  87. struct BlinnPhongShader : public IShader {
  88. Texture *albedoT;
  89. Matrix4 MVP, MV, V, N;
  90. float ambientStrength = 0.05, diffStrength=1 , specularStrength= 0.5;
  91. float diff, spec, shininess = 128;
  92. Vector3f normals[3], viewDir[3], UV[3];
  93. Vector3f ambient, diffuse, specular, interpNormal, interpViewDir, interpUV;
  94. Vector3f lightColor{1,1,1};
  95. Vector3f halfwayDir, lightDir;
  96. Vector3f interpCol, white{255,255,255};
  97. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,const Vector3f &textureVals,const Vector3f &tangent, const Vector3f &light, int index) override{
  98. normals[index] = N.matMultDir(normal).normalized();
  99. UV[index] = textureVals;
  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).normalized();
  107. interpViewDir = viewDir[0] + (viewDir[1] - viewDir[0])* u + (viewDir[2] - viewDir[0]) * v;
  108. interpUV = UV[0] + (UV[1] - UV[0])* u + (UV[2] - UV[0]) * v;
  109. //Albedo
  110. interpCol = albedoT->getPixelVal(interpUV.x, interpUV.y);
  111. //Ambient
  112. ambient = lightColor * ambientStrength;
  113. //Diffuse
  114. diff = std::max(0.0f, interpNormal.dotProduct(lightDir));
  115. diffuse = lightColor * diff * diffStrength;
  116. //Specular
  117. halfwayDir = (lightDir - interpViewDir).normalized();
  118. spec = std::pow(std::max(0.0f, interpNormal.dotProduct(halfwayDir)), shininess);
  119. specular = lightColor * spec * specularStrength;
  120. return (ambient + diffuse) * interpCol + specular * white;
  121. }
  122. };
  123. // Shader that uses texture mapping extensively
  124. struct TextureMapShader : public IShader {
  125. //Variables set per model
  126. Texture *albedoT, *normalT, *ambientOT;
  127. Matrix4 MVP, MV, V, M, N;
  128. Vector3f cameraPos;
  129. //Light Variables
  130. Vector3f lightColor{1,1,1}, white{1,1,1};
  131. float ambientStrength = 0.1, diffStrength = 0.9, specularStrength = 0.8;
  132. float diff, spec, shininess = 128;
  133. Vector3f lightDir[3];
  134. //Variables set per vertex
  135. Vector3f viewDir[3], texCoords[3];
  136. Vector3f normal_WS, tangent_WS, biTangent_WS;
  137. Matrix4 TBN;
  138. //Interpolated variables
  139. Vector3f interpCoords, interpLightDir, interpNormal,
  140. interpViewDir, interpCol, interpAO;
  141. //Per fragment
  142. Vector3f ambient, diffuse, specular ;
  143. Vector3f halfwayDir;
  144. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,const Vector3f &textureVals, const Vector3f &tangent, const Vector3f &light, int index) override{
  145. //Creating TBN matrix
  146. normal_WS = N.matMultDir(normal).normalized();
  147. tangent_WS = N.matMultDir(tangent).normalized();
  148. biTangent_WS = normal_WS.crossProduct(tangent_WS);
  149. TBN = Matrix4::TBNMatrix(tangent_WS, biTangent_WS, normal_WS);
  150. //Getting UV coordinates for use in both albedo and normal textures
  151. texCoords[index] = textureVals;
  152. //Passing all lighting related data to tangent space
  153. lightDir[index] = TBN.matMultVec(light);
  154. viewDir[index] = TBN.matMultVec(cameraPos - M.matMultVec(vertex));
  155. return MVP.matMultVec(vertex);
  156. }
  157. Vector3f fragment(float u, float v) override{
  158. //Interpolated stuff
  159. interpCoords = texCoords[0] + (texCoords[1] - texCoords[0])* u + (texCoords[2] - texCoords[0]) * v;
  160. interpLightDir = lightDir[0] + (lightDir[1] - lightDir[0])* u + (lightDir[2] - lightDir[0]) * v;
  161. interpViewDir = viewDir[0] + (viewDir[1] - viewDir[0])* u + (viewDir[2] - viewDir[0]) * v;
  162. //Reading albedo and normal data from textures
  163. interpCol = albedoT->getPixelVal(interpCoords.x, interpCoords.y);
  164. interpAO = ambientOT->getIntensityVal(interpCoords.x, interpCoords.y);
  165. interpNormal = normalT->getPixelVal(interpCoords.x, interpCoords.y);
  166. interpNormal = interpNormal.normalized();
  167. //Ambient
  168. ambient = lightColor * ambientStrength * interpAO;
  169. //Diffuse
  170. diff = std::max(0.0f, interpNormal.dotProduct(interpLightDir));
  171. diffuse = lightColor * diff * diffStrength;
  172. //Specular
  173. halfwayDir = (interpLightDir + interpViewDir).normalized();
  174. spec = std::pow(std::max(0.0f, interpNormal.dotProduct(halfwayDir)), shininess);
  175. specular = lightColor * spec * specularStrength;
  176. return (ambient + diffuse) * interpCol + specular * white;
  177. }
  178. };
  179. // Shader that uses texture mapping and a PBR approach for shading
  180. // Uses a cook-torrance BRDF for direct light sources.
  181. struct PBRShader : public IShader {
  182. //Variables set per model
  183. Texture *albedoT, *normalT, *ambientOT, *roughT, *metalT;
  184. Matrix4 MVP, MV, V, M, N;
  185. Vector3f cameraPos;
  186. //Light Variables
  187. Vector3f lightColor{1,1,1}, F0{0.04, 0.04, 0.04}, F0corrected; //Default value dielectric
  188. float nDotL, nDotV, ambientInt = 0.01;
  189. Vector3f lightDir[3];
  190. //Variables set per vertex
  191. Vector3f viewDir[3], texCoords[3];
  192. Vector3f normal_WS, tangent_WS, biTangent_WS;
  193. Matrix4 TBN;
  194. //Interpolated variables
  195. Vector3f interpCoords, interpLightDir,
  196. interpNormal, interpViewDir,
  197. interpCol;
  198. //Per fragment
  199. Vector3f F, NDF, G ; //Fresnel, normal distribution function and geometry occlusion
  200. Vector3f halfwayDir, radianceOut, ambient;
  201. Vector3f specular, kD, kS;
  202. float interpRough, interpAO, interpMetal;
  203. float u, v, intPart;
  204. //BRDF functions
  205. Vector3f fresnelSchlick(float cosTheta, Vector3f &fresnel0 ){
  206. return fresnel0 + (Vector3f(1.0)- fresnel0)* std::pow(1.0 - cosTheta, 5.0);
  207. }
  208. float distributionGGX(Vector3f normal, Vector3f halfway, float roughness){
  209. float a = roughness*roughness;
  210. float a2 = a*a;
  211. float NdotH = std::max(normal.dotProduct(halfway), 0.0f);
  212. float NdotH2 = NdotH*NdotH;
  213. float denom = (NdotH2 * (a2 - 1.0) + 1.0);
  214. denom = M_PI * denom * denom;
  215. return a2 / denom;
  216. }
  217. float GeometrySchlickGGX(float NdotV, float roughness){
  218. float r = (roughness + 1.0);
  219. float k = (r*r) / 8.0; //Only useful for direct lighting must be changed in ibr
  220. float denom = NdotV * (1.0 - k) + k;
  221. return NdotV / denom;
  222. }
  223. float GeometrySmith(float roughness){
  224. return GeometrySchlickGGX(nDotL, roughness) * GeometrySchlickGGX(nDotV, roughness);
  225. }
  226. //Vertex shader
  227. Vector3f vertex(const Vector3f &vertex, const Vector3f &normal,const Vector3f &textureVals, const Vector3f &tangent, const Vector3f &light, int index) override{
  228. //Creating TBN matrix
  229. normal_WS = N.matMultDir(normal).normalized();
  230. tangent_WS = N.matMultDir(tangent).normalized();
  231. biTangent_WS = normal_WS.crossProduct(tangent_WS);
  232. TBN = Matrix4::TBNMatrix(tangent_WS, biTangent_WS, normal_WS);
  233. //Getting UV coordinates for use in all textures
  234. texCoords[index] = textureVals;
  235. //Passing all lighting related data to tangent space
  236. lightDir[index] = TBN.matMultVec(light);
  237. viewDir[index] = TBN.matMultVec(cameraPos - M.matMultVec(vertex));
  238. return MVP.matMultVec(vertex);
  239. }
  240. //Fragment shader
  241. Vector3f fragment(float u, float v) override{
  242. //Interpolated stuff
  243. interpCoords = texCoords[0] + (texCoords[1] - texCoords[0])* u + (texCoords[2] - texCoords[0]) * v;
  244. interpLightDir = lightDir[0] + (lightDir[1] - lightDir[0])* u + (lightDir[2] - lightDir[0]) * v;
  245. interpViewDir = viewDir[0] + (viewDir[1] - viewDir[0])* u + (viewDir[2] - viewDir[0]) * v;
  246. //Correcting UV's for tiling
  247. u = std::modf(interpCoords.x, &intPart);
  248. v = std::modf(interpCoords.y, &intPart);
  249. //Reading data from textures for use in lighting calculations
  250. interpCol = albedoT->getPixelVal(u, v);
  251. interpAO = ambientOT->getIntensityVal(u, v);
  252. interpRough = roughT->getIntensityVal(u, v);;
  253. interpMetal = metalT->getIntensityVal(u, v);
  254. interpNormal = normalT->getPixelVal(u, v);
  255. interpNormal = interpNormal.normalized();
  256. interpViewDir = interpViewDir.normalized();
  257. //Setting up Direct Lighting variables
  258. halfwayDir = (interpLightDir + interpViewDir).normalized();
  259. nDotV = std::max(interpNormal.dotProduct(interpViewDir), 0.0f);
  260. nDotL = std::max(interpNormal.dotProduct(interpLightDir), 0.0f);
  261. F0corrected = (F0 * (1.0f-interpMetal)) + (interpCol * interpMetal);//Varying f0 based on metallicness of surface
  262. radianceOut.zero();
  263. //TODO:: Iterate through every light and set radiance
  264. //We assume the only light in the scene is the sun so there is no attenuation
  265. //Setting up BRDF
  266. F = fresnelSchlick(std::max(halfwayDir.dotProduct(interpViewDir), 0.0f), F0corrected);
  267. NDF = distributionGGX(interpNormal, halfwayDir, interpRough);
  268. G = GeometrySmith(interpRough);
  269. //Calculating specular component of BRDF
  270. Vector3f numerator = F * (G*NDF);
  271. float invDenominator = 1 / std::max(4.0 * nDotL * nDotV, 0.001);
  272. specular = numerator * invDenominator;
  273. //Calculating the full rendering equation for a single light
  274. kS = F;
  275. kD = (Vector3f(1.0) - kS);
  276. kD = kD * (1.0f - interpMetal);
  277. radianceOut = ( (kD * (interpCol * (1/M_PI)) + specular ) * nDotL * lightColor);
  278. //Simplistic ambient term
  279. ambient = interpCol * (ambientInt * interpAO);
  280. return ambient + radianceOut;
  281. }
  282. };
  283. #endif