1 /* 2 Copyright (c) 2017-2018 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.graphics.materials.terrain; 29 30 import std.stdio; 31 import std.math; 32 import std.conv; 33 34 import dlib.core.memory; 35 import dlib.math.vector; 36 import dlib.math.matrix; 37 import dlib.image.color; 38 import dlib.image.unmanaged; 39 40 import derelict.opengl; 41 42 import dagon.core.ownership; 43 import dagon.graphics.rc; 44 import dagon.graphics.shadow; 45 import dagon.graphics.clustered; 46 import dagon.graphics.material; 47 import dagon.graphics.materials.generic; 48 49 /* 50 * Backend for terrain material. 51 * Currently supports automatic blending of two textures (grass and mountains) based on slopeness. 52 */ 53 54 class TerrainBackend: GLSLMaterialBackend 55 { 56 private string vsText = " 57 #version 330 core 58 59 layout (location = 0) in vec3 va_Vertex; 60 layout (location = 1) in vec3 va_Normal; 61 layout (location = 2) in vec2 va_Texcoord; 62 63 out vec2 texCoord; 64 out vec3 eyePosition; 65 out vec3 worldPosition; 66 out vec3 eyeNormal; 67 out vec3 worldNormal; 68 69 out vec4 shadowCoord1; 70 out vec4 shadowCoord2; 71 out vec4 shadowCoord3; 72 73 uniform mat4 modelViewMatrix; 74 uniform mat4 normalMatrix; 75 uniform mat4 projectionMatrix; 76 uniform mat4 invViewMatrix; 77 78 uniform mat4 shadowMatrix1; 79 uniform mat4 shadowMatrix2; 80 uniform mat4 shadowMatrix3; 81 82 const float texScale = 100.0; 83 const float eyeSpaceNormalShift = 0.05; 84 85 void main() 86 { 87 texCoord = va_Texcoord * texScale; 88 worldNormal = va_Normal; 89 eyeNormal = (normalMatrix * vec4(va_Normal, 0.0)).xyz; 90 91 vec4 pos = modelViewMatrix * vec4(va_Vertex, 1.0); 92 eyePosition = pos.xyz; 93 94 worldPosition = (invViewMatrix * pos).xyz; 95 96 vec4 posShifted = pos + vec4(eyeNormal * eyeSpaceNormalShift, 0.0); 97 shadowCoord1 = shadowMatrix1 * posShifted; 98 shadowCoord2 = shadowMatrix2 * posShifted; 99 shadowCoord3 = shadowMatrix3 * posShifted; 100 101 gl_Position = projectionMatrix * pos; 102 } 103 "; 104 105 private string fsText = " 106 #version 330 core 107 108 in vec2 texCoord; 109 in vec3 eyePosition; 110 in vec3 worldPosition; 111 in vec3 eyeNormal; 112 in vec3 worldNormal; 113 114 in vec4 shadowCoord1; 115 in vec4 shadowCoord2; 116 in vec4 shadowCoord3; 117 118 out vec4 frag_color; 119 120 uniform mat4 viewMatrix; 121 uniform mat4 invViewMatrix; 122 123 uniform float roughness; 124 125 uniform sampler2D grassTexture; 126 uniform sampler2D mountsTexture; 127 128 uniform sampler2D grassNormalTexture; 129 uniform sampler2D mountsNormalTexture; 130 131 uniform sampler2DArrayShadow shadowTextureArray; 132 uniform float shadowTextureSize; 133 uniform bool useShadows; 134 135 uniform float invLightDomainSize; 136 uniform usampler2D lightClusterTexture; 137 uniform usampler1D lightIndexTexture; 138 uniform sampler2D lightsTexture; 139 140 uniform vec4 environmentColor; 141 uniform vec3 sunDirection; 142 uniform vec3 sunColor; 143 uniform vec4 fogColor; 144 uniform float fogStart; 145 uniform float fogEnd; 146 147 mat3 cotangentFrame(in vec3 N, in vec3 p, in vec2 uv) 148 { 149 vec3 dp1 = dFdx(p); 150 vec3 dp2 = dFdy(p); 151 vec2 duv1 = dFdx(uv); 152 vec2 duv2 = dFdy(uv); 153 vec3 dp2perp = cross(dp2, N); 154 vec3 dp1perp = cross(N, dp1); 155 vec3 T = dp2perp * duv1.x + dp1perp * duv2.x; 156 vec3 B = dp2perp * duv1.y + dp1perp * duv2.y; 157 float invmax = inversesqrt(max(dot(T, T), dot(B, B))); 158 return mat3(T * invmax, B * invmax, N); 159 } 160 161 float shadowLookup(in sampler2DArrayShadow depths, in float layer, in vec4 coord, in vec2 offset) 162 { 163 float texelSize = 1.0 / shadowTextureSize; 164 vec2 v = offset * texelSize * coord.w; 165 vec4 c = (coord + vec4(v.x, v.y, 0.0, 0.0)) / coord.w; 166 c.w = c.z; 167 c.z = layer; 168 float s = texture(depths, c); 169 return s; 170 } 171 172 float pcf(in sampler2DArrayShadow depths, in float layer, in vec4 coord, in float radius, in float yshift) 173 { 174 float s = 0.0; 175 float x, y; 176 for (y = -radius ; y < radius ; y += 1.0) 177 for (x = -radius ; x < radius ; x += 1.0) 178 { 179 s += shadowLookup(depths, layer, coord, vec2(x, y + yshift)); 180 } 181 s /= radius * radius * 4.0; 182 return s; 183 } 184 185 float weight(in vec4 tc) 186 { 187 vec2 proj = vec2(tc.x / tc.w, tc.y / tc.w); 188 proj = (1.0 - abs(proj * 2.0 - 1.0)) * 8.0; 189 proj = clamp(proj, 0.0, 1.0); 190 return min(proj.x, proj.y); 191 } 192 193 void main() 194 { 195 vec3 N = normalize(eyeNormal); 196 vec3 Nw = normalize(worldNormal); 197 vec3 E = normalize(-eyePosition); 198 199 mat3 TBN = cotangentFrame(N, eyePosition, texCoord); 200 vec3 tE = normalize(E * TBN); 201 202 vec3 cameraPosition = invViewMatrix[3].xyz; 203 204 float slope = pow(dot(Nw, vec3(0.0, 1.0, 0.0)), 5.0); 205 206 // Normal mapping 207 vec3 tN1 = normalize(texture(grassNormalTexture, texCoord).rgb * 2.0 - 1.0); 208 tN1.y = -tN1.y; 209 vec3 N1 = normalize(TBN * tN1); 210 211 vec3 tN2 = normalize(texture(mountsNormalTexture, texCoord).rgb * 2.0 - 1.0); 212 tN2.y = -tN2.y; 213 vec3 N2 = normalize(TBN * tN1); 214 215 // Roughness to blinn-phong specular power 216 float gloss = 1.0 - roughness; 217 float shininess = gloss * 128.0; 218 219 // Sun light 220 float sunDiffBrightness1 = clamp(dot(N1, sunDirection), 0.0, 1.0); 221 float sunDiffBrightness2 = clamp(dot(N2, sunDirection), 0.0, 1.0); 222 223 // Calculate shadow from 3 cascades 224 float s1, s2, s3; 225 if (useShadows) 226 { 227 s1 = pcf(shadowTextureArray, 0.0, shadowCoord1, 3.0, 0.0); 228 s2 = pcf(shadowTextureArray, 1.0, shadowCoord2, 2.0, 0.0); 229 s3 = pcf(shadowTextureArray, 2.0, shadowCoord3, 1.0, 0.0); 230 float w1 = weight(shadowCoord1); 231 float w2 = weight(shadowCoord2); 232 float w3 = weight(shadowCoord3); 233 s3 = mix(1.0, s3, w3); 234 s2 = mix(s3, s2, w2); 235 s1 = mix(s2, s1, w1); // s1 stores resulting shadow value 236 } 237 else 238 { 239 s1 = 1.0f; 240 } 241 242 vec3 R = reflect(E, N); 243 244 // Fetch light cluster slice 245 vec2 clusterCoord = (worldPosition.xz - cameraPosition.xz) * invLightDomainSize + 0.5; 246 uint clusterIndex = texture(lightClusterTexture, clusterCoord).r; 247 uint offset = (clusterIndex << 16) >> 16; 248 uint size = (clusterIndex >> 16); 249 250 vec3 pointDiffSum = vec3(0.0, 0.0, 0.0); 251 vec3 pointSpecSum = vec3(0.0, 0.0, 0.0); 252 for (uint i = 0u; i < size; i++) 253 { 254 // Read light data 255 uint u = texelFetch(lightIndexTexture, int(offset + i), 0).r; 256 vec3 lightPos = texelFetch(lightsTexture, ivec2(u, 0), 0).xyz; 257 vec3 lightColor = texelFetch(lightsTexture, ivec2(u, 1), 0).xyz; 258 vec3 lightProps = texelFetch(lightsTexture, ivec2(u, 2), 0).xyz; 259 float lightRadius = lightProps.x; 260 float lightAreaRadius = lightProps.y; 261 float lightEnergy = lightProps.z; 262 263 vec3 lightPosEye = (viewMatrix * vec4(lightPos, 1.0)).xyz; 264 265 vec3 positionToLightSource = lightPosEye - eyePosition; 266 float distanceToLight = length(positionToLightSource); 267 vec3 directionToLight = normalize(positionToLightSource); 268 float attenuation = clamp(1.0 - (distanceToLight / lightRadius), 0.0, 1.0) * lightEnergy; 269 270 float diff = clamp(dot(N, directionToLight), 0.0, 1.0); 271 pointDiffSum += lightColor * diff * attenuation; 272 } 273 274 // Fog 275 float fogDistance = gl_FragCoord.z / gl_FragCoord.w; 276 float fogFactor = clamp((fogEnd - fogDistance) / (fogEnd - fogStart), 0.0, 1.0); 277 278 vec3 colorGrass = texture(grassTexture, texCoord).rgb; //vec3(0.0, 0.5, 0.0); 279 vec3 colorMounts = texture(mountsTexture, texCoord).rgb; 280 vec3 diffColor = mix(colorMounts, colorGrass, slope); 281 282 float diffuse = mix(sunDiffBrightness2, sunDiffBrightness1, slope); 283 284 vec3 objColor = diffColor * (environmentColor.rgb + pointDiffSum + sunColor * diffuse * s1); 285 286 vec3 fragColor = mix(fogColor.rgb, objColor, fogFactor); 287 288 frag_color = vec4(fragColor, 1.0); 289 } 290 "; 291 292 override string vertexShaderSrc() {return vsText;} 293 override string fragmentShaderSrc() {return fsText;} 294 295 GLint viewMatrixLoc; 296 GLint modelViewMatrixLoc; 297 GLint projectionMatrixLoc; 298 GLint normalMatrixLoc; 299 GLint invViewMatrixLoc; 300 301 GLint environmentColorLoc; 302 GLint sunDirectionLoc; 303 GLint sunColorLoc; 304 GLint fogStartLoc; 305 GLint fogEndLoc; 306 GLint fogColorLoc; 307 308 GLint shadowMatrix1Loc; 309 GLint shadowMatrix2Loc; 310 GLint shadowMatrix3Loc; 311 GLint shadowTextureArrayLoc; 312 GLint shadowTextureSizeLoc; 313 GLint useShadowsLoc; 314 315 GLint grassTextureLoc; 316 GLint mountsTextureLoc; 317 GLint grassNormalTextureLoc; 318 319 GLint invLightDomainSizeLoc; 320 GLint clusterTextureLoc; 321 GLint lightsTextureLoc; 322 GLint indexTextureLoc; 323 324 ClusteredLightManager lightManager; 325 CascadedShadowMap shadowMap; 326 Matrix4x4f defaultShadowMat; 327 Vector3f defaultLightDir; 328 329 this(ClusteredLightManager clm, Owner o) 330 { 331 super(o); 332 333 lightManager = clm; 334 335 viewMatrixLoc = glGetUniformLocation(shaderProgram, "viewMatrix"); 336 modelViewMatrixLoc = glGetUniformLocation(shaderProgram, "modelViewMatrix"); 337 projectionMatrixLoc = glGetUniformLocation(shaderProgram, "projectionMatrix"); 338 normalMatrixLoc = glGetUniformLocation(shaderProgram, "normalMatrix"); 339 invViewMatrixLoc = glGetUniformLocation(shaderProgram, "invViewMatrix"); 340 341 environmentColorLoc = glGetUniformLocation(shaderProgram, "environmentColor"); 342 sunDirectionLoc = glGetUniformLocation(shaderProgram, "sunDirection"); 343 sunColorLoc = glGetUniformLocation(shaderProgram, "sunColor"); 344 fogStartLoc = glGetUniformLocation(shaderProgram, "fogStart"); 345 fogEndLoc = glGetUniformLocation(shaderProgram, "fogEnd"); 346 fogColorLoc = glGetUniformLocation(shaderProgram, "fogColor"); 347 348 shadowMatrix1Loc = glGetUniformLocation(shaderProgram, "shadowMatrix1"); 349 shadowMatrix2Loc = glGetUniformLocation(shaderProgram, "shadowMatrix2"); 350 shadowMatrix3Loc = glGetUniformLocation(shaderProgram, "shadowMatrix3"); 351 shadowTextureArrayLoc = glGetUniformLocation(shaderProgram, "shadowTextureArray"); 352 shadowTextureSizeLoc = glGetUniformLocation(shaderProgram, "shadowTextureSize"); 353 useShadowsLoc = glGetUniformLocation(shaderProgram, "useShadows"); 354 355 grassTextureLoc = glGetUniformLocation(shaderProgram, "grassTexture"); 356 mountsTextureLoc = glGetUniformLocation(shaderProgram, "mountsTexture"); 357 grassNormalTextureLoc = glGetUniformLocation(shaderProgram, "grassNormalTexture"); 358 359 clusterTextureLoc = glGetUniformLocation(shaderProgram, "lightClusterTexture"); 360 invLightDomainSizeLoc = glGetUniformLocation(shaderProgram, "invLightDomainSize"); 361 lightsTextureLoc = glGetUniformLocation(shaderProgram, "lightsTexture"); 362 indexTextureLoc = glGetUniformLocation(shaderProgram, "lightIndexTexture"); 363 } 364 365 override void bind(GenericMaterial mat, RenderingContext* rc) 366 { 367 auto igrass = "grass" in mat.inputs; 368 if (igrass is null) 369 igrass = mat.setInput("grass", Color4f(0.0f, 0.5f, 0.0f, 1.0f)); 370 371 auto imounts = "mounts" in mat.inputs; 372 if (imounts is null) 373 imounts = mat.setInput("mounts", Color4f(0.2f, 0.2f, 0.2f, 1.0f)); 374 375 auto igrassNormal = "grassNormal" in mat.inputs; 376 if (igrassNormal is null) 377 igrassNormal = mat.setInput("grassNormal", Color4f(0.0f, 0.0f, 0.0f, 0.0f)); 378 379 bool fogEnabled = boolProp(mat, "fogEnabled"); 380 bool shadowsEnabled = boolProp(mat, "shadowsEnabled"); 381 382 glUseProgram(shaderProgram); 383 384 // Matrices 385 glUniformMatrix4fv(viewMatrixLoc, 1, GL_FALSE, rc.viewMatrix.arrayof.ptr); 386 glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, rc.modelViewMatrix.arrayof.ptr); 387 glUniformMatrix4fv(projectionMatrixLoc, 1, GL_FALSE, rc.projectionMatrix.arrayof.ptr); 388 glUniformMatrix4fv(normalMatrixLoc, 1, GL_FALSE, rc.normalMatrix.arrayof.ptr); 389 glUniformMatrix4fv(invViewMatrixLoc, 1, GL_FALSE, rc.invViewMatrix.arrayof.ptr); 390 391 // Environment parameters 392 Color4f environmentColor = Color4f(0.0f, 0.0f, 0.0f, 1.0f); 393 Vector4f sunHGVector = Vector4f(0.0f, 1.0f, 0.0, 0.0f); 394 Vector3f sunColor = Vector3f(1.0f, 1.0f, 1.0f); 395 if (rc.environment) 396 { 397 environmentColor = rc.environment.ambientConstant; 398 sunHGVector = Vector4f(rc.environment.sunDirection); 399 sunHGVector.w = 0.0; 400 sunColor = rc.environment.sunColor; 401 } 402 glUniform4fv(environmentColorLoc, 1, environmentColor.arrayof.ptr); 403 Vector3f sunDirectionEye = sunHGVector * rc.viewMatrix; 404 glUniform3fv(sunDirectionLoc, 1, sunDirectionEye.arrayof.ptr); 405 glUniform3fv(sunColorLoc, 1, sunColor.arrayof.ptr); 406 Color4f fogColor = Color4f(0.0f, 0.0f, 0.0f, 1.0f); 407 float fogStart = float.max; 408 float fogEnd = float.max; 409 if (fogEnabled) 410 { 411 if (rc.environment) 412 { 413 fogColor = rc.environment.fogColor; 414 fogStart = rc.environment.fogStart; 415 fogEnd = rc.environment.fogEnd; 416 } 417 } 418 glUniform4fv(fogColorLoc, 1, fogColor.arrayof.ptr); 419 glUniform1f(fogStartLoc, fogStart); 420 glUniform1f(fogEndLoc, fogEnd); 421 422 // Texture 0 - grass texture 423 if (igrass.texture is null) 424 { 425 Color4f color = Color4f(igrass.asVector4f); 426 igrass.texture = makeOnePixelTexture(mat, color); 427 } 428 glActiveTexture(GL_TEXTURE0); 429 igrass.texture.bind(); 430 glUniform1i(grassTextureLoc, 0); 431 432 // Texture 1 - mounts texture 433 if (imounts.texture is null) 434 { 435 Color4f color = Color4f(imounts.asVector4f); 436 imounts.texture = makeOnePixelTexture(mat, color); 437 } 438 glActiveTexture(GL_TEXTURE1); 439 imounts.texture.bind(); 440 glUniform1i(mountsTextureLoc, 1); 441 442 // Texture 2 - grass normal map 443 if (igrassNormal.texture is null) 444 { 445 Color4f color = Color4f(0.5f, 0.5f, 1.0f, 0.0f); // default normal pointing upwards 446 igrassNormal.texture = makeOnePixelTexture(mat, color); 447 } 448 glActiveTexture(GL_TEXTURE2); 449 igrassNormal.texture.bind(); 450 glUniform1i(grassNormalTextureLoc, 2); 451 452 // Texture 5 - shadow map cascades (3 layer texture array) 453 if (shadowMap && shadowsEnabled) 454 { 455 glActiveTexture(GL_TEXTURE5); 456 glBindTexture(GL_TEXTURE_2D_ARRAY, shadowMap.depthTexture); 457 458 glUniform1i(shadowTextureArrayLoc, 5); 459 glUniform1f(shadowTextureSizeLoc, cast(float)shadowMap.size); 460 glUniformMatrix4fv(shadowMatrix1Loc, 1, 0, shadowMap.area1.shadowMatrix.arrayof.ptr); 461 glUniformMatrix4fv(shadowMatrix2Loc, 1, 0, shadowMap.area2.shadowMatrix.arrayof.ptr); 462 glUniformMatrix4fv(shadowMatrix3Loc, 1, 0, shadowMap.area3.shadowMatrix.arrayof.ptr); 463 glUniform1i(useShadowsLoc, 1); 464 465 // TODO: shadowFilter 466 } 467 else 468 { 469 glUniformMatrix4fv(shadowMatrix1Loc, 1, 0, defaultShadowMat.arrayof.ptr); 470 glUniformMatrix4fv(shadowMatrix2Loc, 1, 0, defaultShadowMat.arrayof.ptr); 471 glUniformMatrix4fv(shadowMatrix3Loc, 1, 0, defaultShadowMat.arrayof.ptr); 472 glUniform1i(useShadowsLoc, 0); 473 } 474 475 // Texture 6 - light clusters 476 glActiveTexture(GL_TEXTURE6); 477 lightManager.bindClusterTexture(); 478 glUniform1i(clusterTextureLoc, 6); 479 glUniform1f(invLightDomainSizeLoc, lightManager.invSceneSize); 480 481 // Texture 7 - light data 482 glActiveTexture(GL_TEXTURE7); 483 lightManager.bindLightTexture(); 484 glUniform1i(lightsTextureLoc, 7); 485 486 // Texture 8 - light indices per cluster 487 glActiveTexture(GL_TEXTURE8); 488 lightManager.bindIndexTexture(); 489 glUniform1i(indexTextureLoc, 8); 490 } 491 492 override void unbind(GenericMaterial mat, RenderingContext* rc) 493 { 494 auto igrass = "grass" in mat.inputs; 495 auto imounts = "mounts" in mat.inputs; 496 auto igrassNormal = "grassNormal" in mat.inputs; 497 498 glActiveTexture(GL_TEXTURE0); 499 igrass.texture.unbind(); 500 501 glActiveTexture(GL_TEXTURE1); 502 imounts.texture.unbind(); 503 504 glActiveTexture(GL_TEXTURE2); 505 igrassNormal.texture.unbind(); 506 507 glActiveTexture(GL_TEXTURE5); 508 glBindTexture(GL_TEXTURE_2D_ARRAY, 0); 509 510 glActiveTexture(GL_TEXTURE6); 511 lightManager.unbindClusterTexture(); 512 513 glActiveTexture(GL_TEXTURE7); 514 lightManager.unbindLightTexture(); 515 516 glActiveTexture(GL_TEXTURE8); 517 lightManager.unbindIndexTexture(); 518 519 glActiveTexture(GL_TEXTURE0); 520 521 glUseProgram(0); 522 } 523 }