1 /*
2 Copyright (c) 2015 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 module dgl.graphics.shadow;
30 
31 import derelict.opengl.gl;
32 import derelict.opengl.glu;
33 import derelict.opengl.glext;
34 
35 import dlib.core.memory;
36 import dlib.container.array;
37 import dlib.math.vector;
38 import dlib.math.matrix;
39 import dlib.math.affine;
40 import dlib.math.utils;
41 
42 import dgl.core.interfaces;
43 import dgl.graphics.scene;
44 import dgl.graphics.material;
45 
46 class ShadowMap: Drawable
47 {
48     Scene castScene;
49     Scene receiveScene;
50 
51     GLuint depthBuffer;
52     Matrix4x4f lightProjectionMatrix;
53     Matrix4x4f lightViewMatrix;
54     Vector4f lightPosition = Vector4f(0.0f, -1.0f, 0.0f, 0.0f);
55 
56     Matrix4x4f biasMatrix;
57 
58     Vector4f white = Vector4f(1.0f, 1.0f, 1.0f, 1.0f);
59     Vector4f c = Vector4f(0.0f, 0.1f, 0.2f, 1.0f);
60     Vector4f black = Vector4f(0.0f, 0.0f, 0.0f, 0.0f);
61 
62     uint width, height;
63     GLint[4] viewport;
64 
65     float ofsFactor = 4.7;
66     float ofsUnits = 5.3;
67 
68     bool useShader = true;
69 
70     this(uint w, uint h)
71     {
72         width = w;
73         height = h;
74 
75         //glDepthFunc(GL_LEQUAL);
76         glEnable(GL_DEPTH_TEST);
77 
78         glGenTextures(1, &depthBuffer);
79         glBindTexture(GL_TEXTURE_2D, depthBuffer);
80         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //GL_NEAREST for sharp edges
81         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //GL_NEAREST for sharp edges
82         //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
83         //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
84 
85         //Enable shadow comparison
86         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
87         //Shadow comparison should be true (i.e. not in shadow) if r <= texture
88         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
89         //Shadow comparison should generate an INTENSITY result
90         glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
91 
92         glTexImage2D(GL_TEXTURE_2D, 0,
93            GL_DEPTH_COMPONENT, width, height, 0,
94            GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, null);
95         //glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null);
96 
97         glLoadIdentity();
98         float size = 20;
99         glOrtho(-size, size, -size, size, -20.0f, 100.0f);
100         glGetFloatv(GL_MODELVIEW_MATRIX, lightProjectionMatrix.arrayof.ptr);
101         glLoadIdentity();
102 
103         Vector3f v1 = Vector3f(1, -1, 1).normalized;
104         Vector3f v2 = cross(v1, Vector3f(0, 1, 0));
105         up = cross(v2, v1);
106     }
107 
108     Vector3f up;
109 
110     void draw(double dt)
111     {
112         glPushMatrix();
113         glLoadIdentity();
114         Vector3f toVector = Vector3f(lightPosition.x + 1, -1, lightPosition.z + 1);
115         gluLookAt(lightPosition.x, 0, lightPosition.z,
116             toVector.x, toVector.y, toVector.z,
117             up.x, up.y, up.z);
118         glGetFloatv(GL_MODELVIEW_MATRIX, lightViewMatrix.arrayof.ptr);
119         glPopMatrix();
120 
121         renderDepthBuffer(dt);
122 
123         glClear(GL_DEPTH_BUFFER_BIT);
124 
125         //Use dim light to represent shadowed areas
126         glLightfv(GL_LIGHT7, GL_POSITION, lightPosition.arrayof.ptr);
127         glLightfv(GL_LIGHT7, GL_AMBIENT, c.arrayof.ptr);
128         glLightfv(GL_LIGHT7, GL_DIFFUSE, c.arrayof.ptr);
129         glLightfv(GL_LIGHT7, GL_SPECULAR, black.arrayof.ptr);
130         glEnable(GL_LIGHT7);
131         glEnable(GL_LIGHTING);
132 
133         if (receiveScene)
134         {
135             receiveScene.lighted = false;
136             dgl.graphics.material.useDimLight = true;
137             receiveScene.draw(dt);
138         }
139         if (castScene)
140         {
141             castScene.lighted = false;
142             dgl.graphics.material.useDimLight = true;
143             castScene.draw(dt);
144         }
145 
146         //Draw with bright light
147         glLightfv(GL_LIGHT7, GL_DIFFUSE, white.arrayof.ptr);
148         glLightfv(GL_LIGHT7, GL_SPECULAR, white.arrayof.ptr);
149         glDisable(GL_LIGHT7);
150         glDisable(GL_LIGHTING);
151 
152         //Bind & enable shadow map texture
153         glActiveTextureARB(GL_TEXTURE3_ARB);
154         bindDepthBuffer();
155 
156         //Matrix4x4f modelView;
157         //glGetFloatv(GL_MODELVIEW_MATRIX, modelView.arrayof.ptr);
158         //Matrix4x4f invViewMartrix = modelView.inverse();
159 
160         // Calculate texture matrix for projection
161         // This matrix takes us from eye space to the light's clip space
162         // It is postmultiplied by the inverse of the current view matrix when specifying texgen
163         //Matrix4x4f textureMatrix = biasMatrix * lightProjectionMatrix * lightViewMatrix;
164         glMatrixMode(GL_TEXTURE);
165         glPushMatrix();
166         glLoadIdentity();
167         glTranslatef(0.5f, 0.5f, 0.5f); // bias matrix
168         glScalef(0.5f, 0.5f, 0.5f);
169         glMultMatrixf(lightProjectionMatrix.arrayof.ptr);
170         glMultMatrixf(lightViewMatrix.arrayof.ptr);
171         //glMultMatrixf(invViewMartrix.arrayof.ptr);
172         glMatrixMode(GL_MODELVIEW);
173         
174         //Set up texture coordinate generation.
175         auto ide = Matrix4x4f.identity;
176         
177         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
178         glTexGenfv(GL_S, GL_EYE_PLANE, ide.getRow(0).arrayof.ptr);
179         glEnable(GL_TEXTURE_GEN_S);
180 
181         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
182         glTexGenfv(GL_T, GL_EYE_PLANE, ide.getRow(1).arrayof.ptr);
183         glEnable(GL_TEXTURE_GEN_T);
184 
185         glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
186         glTexGenfv(GL_R, GL_EYE_PLANE, ide.getRow(2).arrayof.ptr);
187         glEnable(GL_TEXTURE_GEN_R);
188 
189         glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
190         glTexGenfv(GL_Q, GL_EYE_PLANE, ide.getRow(3).arrayof.ptr);
191         glEnable(GL_TEXTURE_GEN_Q);
192 
193         //Set alpha test to discard false comparisons
194         //glAlphaFunc(GL_GREATER, 0.7f);
195         //glEnable(GL_ALPHA_TEST);
196 
197         glActiveTextureARB(GL_TEXTURE0_ARB);
198 
199         glEnable(GL_POLYGON_OFFSET_FILL);
200         glPolygonOffset(ofsFactor, ofsUnits);
201 
202         if (castScene)
203         {
204             castScene.lighted = true;
205             dgl.graphics.material.useDimLight = false;
206             castScene.draw(dt);
207         }
208         if (receiveScene)
209         {
210             receiveScene.lighted = true;
211             dgl.graphics.material.useDimLight = false;
212             receiveScene.draw(dt);
213         }
214 
215         //glPolygonOffset(0, 0);
216 
217         //Disable textures and texgen
218         glActiveTextureARB(GL_TEXTURE3_ARB);
219         unbindDepthBuffer();
220 
221         glDisable(GL_TEXTURE_GEN_S);
222         glDisable(GL_TEXTURE_GEN_T);
223         glDisable(GL_TEXTURE_GEN_R);
224         glDisable(GL_TEXTURE_GEN_Q);
225 
226         glMatrixMode(GL_TEXTURE);
227         glPopMatrix();
228         glMatrixMode(GL_MODELVIEW);
229 
230         glActiveTextureARB(GL_TEXTURE0_ARB);
231 
232         //Restore other states
233         glDisable(GL_LIGHTING);
234         glDisable(GL_ALPHA_TEST);
235     }
236 
237     void renderDepthBuffer(double dt)
238     {
239         //glCullFace(GL_FRONT);
240         glShadeModel(GL_FLAT);
241         glColorMask(0, 0, 0, 0);
242 
243         glGetIntegerv(GL_VIEWPORT, viewport.ptr);
244         glViewport(0, 0, width, height);
245 
246         glMatrixMode(GL_PROJECTION);
247         glPushMatrix();
248         glLoadMatrixf(lightProjectionMatrix.arrayof.ptr);
249         glMatrixMode(GL_MODELVIEW);
250         glPushMatrix();
251         glLoadMatrixf(lightViewMatrix.arrayof.ptr);
252 
253         // Draw the scene
254         if (castScene)
255         {
256             dgl.graphics.material.useDimLight = true;
257             castScene.draw(dt);
258             dgl.graphics.material.useDimLight = false;
259         }
260 
261         glPopMatrix();
262         glMatrixMode(GL_PROJECTION);
263         glPopMatrix();
264         glMatrixMode(GL_MODELVIEW);
265 
266         //Read the depth buffer into the shadow map texture
267         glEnable(GL_TEXTURE_2D);
268         glBindTexture(GL_TEXTURE_2D, depthBuffer);
269         glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height);
270         glBindTexture(GL_TEXTURE_2D, 0);
271         glDisable(GL_TEXTURE_2D);
272 
273         glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
274 
275         //restore states
276         //glCullFace(GL_BACK);
277         glShadeModel(GL_SMOOTH);
278         glColorMask(1, 1, 1, 1);
279     }
280 
281     void free()
282     {
283         Delete(this);
284     }
285 
286     ~this()
287     {
288         if (glIsTexture(depthBuffer))
289             glDeleteTextures(1, &depthBuffer);
290     }
291 
292     void bindDepthBuffer()
293     {
294         glEnable(GL_TEXTURE_2D);
295         glBindTexture(GL_TEXTURE_2D, depthBuffer);
296     }
297 
298     void unbindDepthBuffer()
299     {
300         glBindTexture(GL_TEXTURE_2D, 0);
301         glDisable(GL_TEXTURE_2D);
302     }
303 }