1 /*
2 Copyright (c) 2020 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.render.shaders.forward;
29 
30 import std.stdio;
31 import std.math;
32 
33 import dlib.core.memory;
34 import dlib.core.ownership;
35 import dlib.math.vector;
36 import dlib.math.matrix;
37 import dlib.math.transformation;
38 import dlib.math.interpolation;
39 import dlib.image.color;
40 import dlib.text.str;
41 
42 import dagon.core.bindings;
43 import dagon.graphics.material;
44 import dagon.graphics.light;
45 import dagon.graphics.shader;
46 import dagon.graphics.state;
47 import dagon.graphics.cubemap;
48 import dagon.graphics.csm;
49 
50 class ForwardShader: Shader
51 {
52     String vs, fs;
53 
54     Matrix4x4f defaultShadowMatrix;
55     GLuint defaultShadowTexture;
56 
57     this(Owner owner)
58     {
59         vs = Shader.load("data/__internal/shaders/Forward/Forward.vert.glsl");
60         fs = Shader.load("data/__internal/shaders/Forward/Forward.frag.glsl");
61 
62         auto prog = New!ShaderProgram(vs, fs, this);
63         super(prog, owner);
64 
65         defaultShadowMatrix = Matrix4x4f.identity;
66 
67         glGenTextures(1, &defaultShadowTexture);
68         glActiveTexture(GL_TEXTURE0);
69         glBindTexture(GL_TEXTURE_2D_ARRAY, defaultShadowTexture);
70         glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, 1, 1, 3, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null);
71         glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
72         glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
73         glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
74     }
75 
76     ~this()
77     {
78         if (glIsFramebuffer(defaultShadowTexture))
79             glDeleteFramebuffers(1, &defaultShadowTexture);
80 
81         vs.free();
82         fs.free();
83     }
84 
85     override void bindParameters(GraphicsState* state)
86     {
87         auto idiffuse = "diffuse" in state.material.inputs;
88         auto inormal = "normal" in state.material.inputs;
89         auto iheight = "height" in state.material.inputs;
90         auto iroughnessMetallic = "roughnessMetallic" in state.material.inputs;
91         //auto ipbr = "pbr" in state.material.inputs;
92         auto iroughness = "roughness" in state.material.inputs;
93         auto imetallic = "metallic" in state.material.inputs;
94         auto ispecularity = "specularity" in state.material.inputs;
95         auto itransparency = "transparency" in state.material.inputs;
96         auto itranslucency = "translucency" in state.material.inputs;
97         auto itextureScale = "textureScale" in state.material.inputs;
98         auto iparallax = "parallax" in state.material.inputs;
99         auto iemission = "emission" in state.material.inputs;
100         auto ienergy = "energy" in state.material.inputs;
101         auto isphericalNormal = "sphericalNormal" in state.material.inputs;
102         auto ishadeless = "shadeless" in state.material.inputs;
103 
104         setParameter("modelViewMatrix", state.modelViewMatrix);
105         setParameter("projectionMatrix", state.projectionMatrix);
106         setParameter("normalMatrix", state.normalMatrix);
107         setParameter("viewMatrix", state.viewMatrix);
108         setParameter("invViewMatrix", state.invViewMatrix);
109         setParameter("prevModelViewMatrix", state.prevModelViewMatrix);
110 
111         setParameter("layer", cast(float)(state.layer));
112         
113         float materialOpacity = 1.0f;
114         if (itransparency)
115             materialOpacity = itransparency.asFloat;
116         setParameter("opacity", state.opacity * materialOpacity);
117         
118         setParameter("textureScale", itextureScale.asVector2f);
119 
120         setParameter("viewSize", state.resolution);
121 
122         // Sun
123         Light sun = state.material.sun;
124         if (sun is null)
125             sun = state.environment.sun;
126         
127         Vector3f sunDirection = Vector3f(0.0f, 0.0f, 1.0f);
128         Color4f sunColor = Color4f(1.0f, 1.0f, 1.0f, 1.0f);
129         float sunEnergy = 1.0f;
130         bool sunScatteringEnabled = false;
131         float sunScatteringG = 0.0f;
132         float sunScatteringDensity = 1.0f;
133         int sunScatteringSamples = 1;
134         float sunScatteringMaxRandomStepOffset = 0.0f;
135         bool sunScatteringShadow = false;
136         bool shaded = !ishadeless.asBool;
137         if (sun)
138         {
139             sunDirection = sun.directionAbsolute;
140             sunColor = sun.color;
141             sunEnergy = sun.energy;
142             sunScatteringG = 1.0f - sun.scattering;
143             sunScatteringDensity = sun.mediumDensity;
144             sunScatteringEnabled = sun.scatteringEnabled;
145             sunScatteringSamples = sun.scatteringSamples;
146             sunScatteringMaxRandomStepOffset = sun.scatteringMaxRandomStepOffset;
147             sunScatteringShadow = sun.scatteringUseShadow;
148         }
149         Vector4f sunDirHg = Vector4f(sunDirection);
150         sunDirHg.w = 0.0;
151         setParameter("sunDirection", (sunDirHg * state.viewMatrix).xyz);
152         setParameter("sunColor", sunColor);
153         setParameter("sunEnergy", sunEnergy);
154         setParameter("sunScatteringG", sunScatteringG);
155         setParameter("sunScatteringDensity", sunScatteringDensity);
156         setParameter("sunScattering", sunScatteringEnabled);
157         setParameter("sunScatteringSamples", sunScatteringSamples);
158         setParameter("sunScatteringMaxRandomStepOffset", sunScatteringMaxRandomStepOffset);
159         setParameter("sunScatteringShadow", sunScatteringShadow);
160         setParameter("shaded", shaded);
161 
162         setParameter("time", state.localTime);
163 
164         // Parallax mode
165         int parallaxMethod = iparallax.asInteger;
166         if (parallaxMethod > ParallaxOcclusionMapping)
167             parallaxMethod = ParallaxOcclusionMapping;
168         if (parallaxMethod < 0)
169             parallaxMethod = 0;
170 
171         setParameter("sphericalNormal", cast(int)isphericalNormal.asBool);
172 
173         // Diffuse
174         if (idiffuse.texture)
175         {
176             glActiveTexture(GL_TEXTURE0);
177             idiffuse.texture.bind();
178             setParameter("diffuseTexture", cast(int)0);
179             setParameterSubroutine("diffuse", ShaderType.Fragment, "diffuseColorTexture");
180         }
181         else
182         {
183             setParameter("diffuseVector", idiffuse.asVector4f);
184             setParameterSubroutine("diffuse", ShaderType.Fragment, "diffuseColorValue");
185         }
186 
187         // Normal/height
188         bool haveHeightMap = inormal.texture !is null;
189         if (haveHeightMap)
190             haveHeightMap = inormal.texture.image.channels == 4;
191 
192         if (!haveHeightMap)
193         {
194             if (inormal.texture is null)
195             {
196                 if (iheight.texture !is null) // we have height map, but no normal map
197                 {
198                     Color4f color = Color4f(0.5f, 0.5f, 1.0f, 0.0f); // default normal pointing upwards
199                     inormal.texture = state.material.makeTexture(color, iheight.texture);
200                     haveHeightMap = true;
201                 }
202             }
203             else
204             {
205                 if (iheight.texture !is null) // we have both normal and height maps
206                 {
207                     inormal.texture = state.material.makeTexture(inormal.texture, iheight.texture);
208                     haveHeightMap = true;
209                 }
210             }
211         }
212 
213         if (inormal.texture)
214         {
215             setParameter("generateTBN", 1);
216             setParameter("normalTexture", 1);
217             setParameterSubroutine("normal", ShaderType.Fragment, "normalMap");
218 
219             glActiveTexture(GL_TEXTURE1);
220             inormal.texture.bind();
221         }
222         else
223         {
224             setParameter("generateTBN", 0);
225             setParameter("normalVector", state.material.normal.asVector3f);
226             setParameterSubroutine("normal", ShaderType.Fragment, "normalValue");
227         }
228         
229         if (state.material.invertNormalY)
230             setParameter("normalYSign", -1.0f);
231         else
232             setParameter("normalYSign", 1.0f);
233 
234         // Height and parallax
235         // TODO: make these material properties
236         float parallaxScale = 0.03f;
237         float parallaxBias = -0.01f;
238         setParameter("parallaxScale", parallaxScale);
239         setParameter("parallaxBias", parallaxBias);
240 
241         if (haveHeightMap)
242         {
243             setParameterSubroutine("height", ShaderType.Fragment, "heightMap");
244         }
245         else
246         {
247             float h = 0.0f; //-parallaxBias / parallaxScale;
248             setParameter("heightScalar", h);
249             setParameterSubroutine("height", ShaderType.Fragment, "heightValue");
250             parallaxMethod = ParallaxNone;
251         }
252 
253         if (parallaxMethod == ParallaxSimple)
254             setParameterSubroutine("parallax", ShaderType.Fragment, "parallaxSimple");
255         else if (parallaxMethod == ParallaxOcclusionMapping)
256             setParameterSubroutine("parallax", ShaderType.Fragment, "parallaxOcclusionMapping");
257         else
258             setParameterSubroutine("parallax", ShaderType.Fragment, "parallaxNone");
259         
260         // PBR
261         if (iroughnessMetallic is null)
262         {
263             state.material.setInput("roughnessMetallic", 0.0f);
264             iroughnessMetallic = "roughnessMetallic" in state.material.inputs;
265         }
266         if (iroughnessMetallic.texture is null)
267         {
268             iroughnessMetallic.texture = state.material.makeTexture(*ispecularity, *iroughness, *imetallic, *itranslucency);
269         }
270         glActiveTexture(GL_TEXTURE2);
271         iroughnessMetallic.texture.bind();
272         setParameter("pbrTexture", 2);
273         
274         setParameterSubroutine("specularity", ShaderType.Fragment, "specularityMap");
275         setParameterSubroutine("metallic", ShaderType.Fragment, "metallicMap");
276         setParameterSubroutine("roughness", ShaderType.Fragment, "roughnessMap");
277         
278         if (itranslucency.texture is null)
279         {
280             setParameterSubroutine("translucency", ShaderType.Fragment, "translucencyValue");
281 
282             if (itranslucency.type == MaterialInputType.Float)
283                 setParameter("translucencyScalar", itranslucency.asFloat);
284             else if (itranslucency.type == MaterialInputType.Bool)
285                 setParameter("translucencyScalar", cast(float)itranslucency.asBool);
286             else if (itranslucency.type == MaterialInputType.Integer)
287                 setParameter("translucencyScalar", cast(float)itranslucency.asInteger);
288             else if (itranslucency.type == MaterialInputType.Vec2)
289                 setParameter("translucencyScalar", itranslucency.asVector2f.r);
290             else if (itranslucency.type == MaterialInputType.Vec3)
291                 setParameter("translucencyScalar", itranslucency.asVector3f.r);
292             else if (itranslucency.type == MaterialInputType.Vec4)
293                 setParameter("translucencyScalar", itranslucency.asVector4f.r);
294         }
295         else
296         {
297             setParameterSubroutine("translucency", ShaderType.Fragment, "translucencyMap");
298         }
299 
300         // Emission
301         if (iemission.texture)
302         {
303             glActiveTexture(GL_TEXTURE3);
304             iemission.texture.bind();
305             setParameter("emissionTexture", cast(int)3);
306             setParameterSubroutine("emission", ShaderType.Fragment, "emissionColorTexture");
307         }
308         else
309         {
310             setParameter("emissionVector", iemission.asVector4f);
311             setParameterSubroutine("emission", ShaderType.Fragment, "emissionColorValue");
312         }
313         setParameter("energy", ienergy.asFloat);
314 
315         // Environment
316         if (state.environment)
317         {
318             setParameter("fogColor", state.environment.fogColor);
319             setParameter("fogStart", state.environment.fogStart);
320             setParameter("fogEnd", state.environment.fogEnd);
321             setParameter("ambientEnergy", state.environment.ambientEnergy);
322 
323             if (state.environment.ambientMap)
324             {
325                 glActiveTexture(GL_TEXTURE4);
326                 state.environment.ambientMap.bind();
327                 if (cast(Cubemap)state.environment.ambientMap)
328                 {
329                     setParameter("ambientTextureCube", 4);
330                     setParameterSubroutine("ambient", ShaderType.Fragment, "ambientCubemap");
331                 }
332                 else
333                 {
334                     setParameter("ambientTexture", 4);
335                     setParameterSubroutine("ambient", ShaderType.Fragment, "ambientEquirectangularMap");
336                 }
337             }
338             else
339             {
340                 setParameter("ambientVector", state.environment.ambientColor);
341                 setParameterSubroutine("ambient", ShaderType.Fragment, "ambientColor");
342             }
343         }
344         else
345         {
346             setParameter("fogColor", Color4f(0.5f, 0.5f, 0.5f, 1.0f));
347             setParameter("fogStart", 0.0f);
348             setParameter("fogEnd", 1000.0f);
349             setParameter("ambientEnergy", 1.0f);
350             setParameter("ambientVector", Color4f(0.5f, 0.5f, 0.5f, 1.0f));
351             setParameterSubroutine("ambient", ShaderType.Fragment, "ambientColor");
352         }
353 
354         // Shadow map
355         if (sun)
356         {
357             if (sun.shadowEnabled)
358             {
359                 CascadedShadowMap csm = cast(CascadedShadowMap)sun.shadowMap;
360 
361                 glActiveTexture(GL_TEXTURE5);
362                 glBindTexture(GL_TEXTURE_2D_ARRAY, csm.depthTexture);
363                 setParameter("shadowTextureArray", 5);
364                 setParameter("shadowResolution", cast(float)csm.resolution);
365                 setParameter("shadowMatrix1", csm.area[0].shadowMatrix);
366                 setParameter("shadowMatrix2", csm.area[1].shadowMatrix);
367                 setParameter("shadowMatrix3", csm.area[2].shadowMatrix);
368                 setParameterSubroutine("shadowMap", ShaderType.Fragment, "shadowMapCascaded");
369             }
370             else
371             {
372                 glActiveTexture(GL_TEXTURE5);
373                 glBindTexture(GL_TEXTURE_2D_ARRAY, defaultShadowTexture);
374                 setParameter("shadowTextureArray", 5);
375                 setParameter("shadowMatrix1", defaultShadowMatrix);
376                 setParameter("shadowMatrix2", defaultShadowMatrix);
377                 setParameter("shadowMatrix3", defaultShadowMatrix);
378                 setParameterSubroutine("shadowMap", ShaderType.Fragment, "shadowMapNone");
379             }
380         }
381         else
382         {
383             glActiveTexture(GL_TEXTURE5);
384             glBindTexture(GL_TEXTURE_2D_ARRAY, defaultShadowTexture);
385             setParameter("shadowTextureArray", 5);
386             setParameter("shadowMatrix1", defaultShadowMatrix);
387             setParameter("shadowMatrix2", defaultShadowMatrix);
388             setParameter("shadowMatrix3", defaultShadowMatrix);
389             setParameterSubroutine("shadowMap", ShaderType.Fragment, "shadowMapNone");
390         }
391 
392         super.bindParameters(state);
393     }
394 
395     override void unbindParameters(GraphicsState* state)
396     {
397         super.unbindParameters(state);
398 
399         glActiveTexture(GL_TEXTURE0);
400         glBindTexture(GL_TEXTURE_2D, 0);
401 
402         glActiveTexture(GL_TEXTURE1);
403         glBindTexture(GL_TEXTURE_2D, 0);
404 
405         glActiveTexture(GL_TEXTURE2);
406         glBindTexture(GL_TEXTURE_2D, 0);
407 
408         glActiveTexture(GL_TEXTURE3);
409         glBindTexture(GL_TEXTURE_2D, 0);
410 
411         glActiveTexture(GL_TEXTURE4);
412         glBindTexture(GL_TEXTURE_2D, 0);
413         glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
414 
415         glActiveTexture(GL_TEXTURE5);
416         glBindTexture(GL_TEXTURE_2D, 0);
417 
418         glActiveTexture(GL_TEXTURE0);
419     }
420 }