|
| 1 | +unit App; |
| 2 | + |
| 3 | +{$INCLUDE 'Sample.inc'} |
| 4 | + |
| 5 | +interface |
| 6 | + |
| 7 | +uses |
| 8 | + System.Classes, |
| 9 | + System.UITypes, |
| 10 | + System.SysUtils, |
| 11 | + {$INCLUDE 'OpenGL.inc'} |
| 12 | + Sample.Classes, |
| 13 | + Sample.App; |
| 14 | + |
| 15 | +type |
| 16 | + TLightCastersApp = class(TApplication) |
| 17 | + private |
| 18 | + FCamera: ICamera; |
| 19 | + FLightingShader: IShader; |
| 20 | + FContainerVAO: IVertexArray; |
| 21 | + FDiffuseMap: GLuint; |
| 22 | + FSpecularMap: GLuint; |
| 23 | + FUniformViewPos: GLint; |
| 24 | + FUniformLightPosition: GLint; |
| 25 | + FUniformLightDirection: GLint; |
| 26 | + FUniformLightCutOff: GLint; |
| 27 | + FUniformLightOuterCutOff: GLint; |
| 28 | + FUniformLightConstant: GLint; |
| 29 | + FUniformLightLinear: GLint; |
| 30 | + FUniformLightQuadratic: GLint; |
| 31 | + FUniformLightAmbient: GLint; |
| 32 | + FUniformLightDiffuse: GLint; |
| 33 | + FUniformLightSpecular: GLint; |
| 34 | + FUniformMaterialShininess: GLint; |
| 35 | + FUniformContainerModel: GLint; |
| 36 | + FUniformContainerView: GLint; |
| 37 | + FUniformContainerProjection: GLint; |
| 38 | + FUniformContainerNormalMatrix: GLint; |
| 39 | + public |
| 40 | + procedure Initialize; override; |
| 41 | + procedure Update(const ADeltaTimeSec, ATotalTimeSec: Double); override; |
| 42 | + procedure Shutdown; override; |
| 43 | + procedure Resize(const AWidth, AHeight: Integer); override; |
| 44 | + public |
| 45 | + procedure KeyDown(const AKey: Integer; const AShift: TShiftState); override; |
| 46 | + procedure KeyUp(const AKey: Integer; const AShift: TShiftState); override; |
| 47 | + procedure MouseDown(const AButton: TMouseButton; const AShift: TShiftState; |
| 48 | + const AX, AY: Single); override; |
| 49 | + procedure MouseMove(const AShift: TShiftState; const AX, AY: Single); override; |
| 50 | + procedure MouseUp(const AButton: TMouseButton; const AShift: TShiftState; |
| 51 | + const AX, AY: Single); override; |
| 52 | + procedure MouseWheel(const AShift: TShiftState; const AWheelDelta: Integer); override; |
| 53 | + end; |
| 54 | + |
| 55 | +implementation |
| 56 | + |
| 57 | +uses |
| 58 | + Neslib.Stb.Image, |
| 59 | + Neslib.FastMath; |
| 60 | + |
| 61 | +const |
| 62 | + { Each container vertex consists of a 3-element position and a 3-element |
| 63 | + normal and a 2-element texture coordinate. |
| 64 | + Each group of 4 vertices defines a side of a cube. } |
| 65 | + CONTAINER_VERTICES: array [0..191] of Single = ( |
| 66 | + // Positions // Normals // Texture Coords |
| 67 | + -0.5, -0.5, -0.5, 0.0, 0.0, -1.0, 0.0, 0.0, |
| 68 | + 0.5, -0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 0.0, |
| 69 | + 0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 1.0, |
| 70 | + -0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 0.0, 1.0, |
| 71 | + |
| 72 | + -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, |
| 73 | + 0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 0.0, |
| 74 | + 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 1.0, |
| 75 | + -0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 1.0, |
| 76 | + |
| 77 | + -0.5, 0.5, 0.5, -1.0, 0.0, 0.0, 1.0, 0.0, |
| 78 | + -0.5, 0.5, -0.5, -1.0, 0.0, 0.0, 1.0, 1.0, |
| 79 | + -0.5, -0.5, -0.5, -1.0, 0.0, 0.0, 0.0, 1.0, |
| 80 | + -0.5, -0.5, 0.5, -1.0, 0.0, 0.0, 0.0, 0.0, |
| 81 | + |
| 82 | + 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0, 0.0, |
| 83 | + 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, 1.0, 1.0, |
| 84 | + 0.5, -0.5, -0.5, 1.0, 0.0, 0.0, 0.0, 1.0, |
| 85 | + 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0.0, |
| 86 | + |
| 87 | + -0.5, -0.5, -0.5, 0.0, -1.0, 0.0, 0.0, 1.0, |
| 88 | + 0.5, -0.5, -0.5, 0.0, -1.0, 0.0, 1.0, 1.0, |
| 89 | + 0.5, -0.5, 0.5, 0.0, -1.0, 0.0, 1.0, 0.0, |
| 90 | + -0.5, -0.5, 0.5, 0.0, -1.0, 0.0, 0.0, 0.0, |
| 91 | + |
| 92 | + -0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 0.0, 1.0, |
| 93 | + 0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 1.0, 1.0, |
| 94 | + 0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 0.0, |
| 95 | + -0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 0.0); |
| 96 | + |
| 97 | +const |
| 98 | + { The indices define 2 triangles per cube face, 6 faces total } |
| 99 | + INDICES: array [0..35] of UInt16 = ( |
| 100 | + 0, 1, 2, 2, 3, 0, |
| 101 | + 4, 5, 6, 6, 7, 4, |
| 102 | + 8, 9, 10, 10, 11, 8, |
| 103 | + 12, 13, 14, 14, 15, 12, |
| 104 | + 16, 17, 18, 18, 19, 16, |
| 105 | + 20, 21, 22, 22, 23, 20); |
| 106 | + |
| 107 | +const |
| 108 | + // Positions all containers |
| 109 | + CUBE_POSITIONS: array [0..9] of TVector3 = ( |
| 110 | + (X: 0.0; Y: 0.0; Z: 0.0), |
| 111 | + (X: 2.0; Y: 5.0; Z: -15.0), |
| 112 | + (X: -1.5; Y: -2.2; Z: -2.5), |
| 113 | + (X: -3.8; Y: -2.0; Z: -12.3), |
| 114 | + (X: 2.4; Y: -0.4; Z: -3.5), |
| 115 | + (X: -1.7; Y: 3.0; Z: -7.5), |
| 116 | + (X: 1.3; Y: -2.0; Z: -2.5), |
| 117 | + (X: 1.5; Y: 2.0; Z: -2.5), |
| 118 | + (X: 1.5; Y: 0.2; Z: -1.5), |
| 119 | + (X: -1.3; Y: 1.0; Z: -1.5)); |
| 120 | + |
| 121 | +{ TLightCastersApp } |
| 122 | + |
| 123 | +procedure TLightCastersApp.Initialize; |
| 124 | +var |
| 125 | + VertexLayout: TVertexLayout; |
| 126 | + Data: TBytes; |
| 127 | + Image: Pointer; |
| 128 | + ImageWidth, ImageHeight, ImageComponents: Integer; |
| 129 | +begin |
| 130 | + { Initialize the asset manager } |
| 131 | + TAssets.Initialize; |
| 132 | + |
| 133 | + { Enable depth testing } |
| 134 | + glEnable(GL_DEPTH_TEST); |
| 135 | + |
| 136 | + { Create camera } |
| 137 | + FCamera := TCamera.Create(Width, Height, Vector3(0, 0, 3)); |
| 138 | + |
| 139 | + { Build and compile our shader programs } |
| 140 | + FLightingShader := TShader.Create('shaders/light_casters.vs', 'shaders/light_casters.fs'); |
| 141 | + FUniformViewPos := FLightingShader.GetUniformLocation('viewPos'); |
| 142 | + FUniformLightPosition := FLightingShader.GetUniformLocation('light.position'); |
| 143 | + FUniformLightDirection := FLightingShader.GetUniformLocation('light.direction'); |
| 144 | + FUniformLightCutOff := FLightingShader.GetUniformLocation('light.cutOff'); |
| 145 | + FUniformLightOuterCutOff := FLightingShader.GetUniformLocation('light.outerCutOff'); |
| 146 | + FUniformLightConstant := FLightingShader.GetUniformLocation('light.constant'); |
| 147 | + FUniformLightLinear := FLightingShader.GetUniformLocation('light.linear'); |
| 148 | + FUniformLightQuadratic := FLightingShader.GetUniformLocation('light.quadratic'); |
| 149 | + FUniformLightAmbient := FLightingShader.GetUniformLocation('light.ambient'); |
| 150 | + FUniformLightDiffuse := FLightingShader.GetUniformLocation('light.diffuse'); |
| 151 | + FUniformLightSpecular := FLightingShader.GetUniformLocation('light.specular'); |
| 152 | + FUniformMaterialShininess := FLightingShader.GetUniformLocation('material.shininess'); |
| 153 | + FUniformContainerModel := FLightingShader.GetUniformLocation('model'); |
| 154 | + FUniformContainerView := FLightingShader.GetUniformLocation('view'); |
| 155 | + FUniformContainerProjection := FLightingShader.GetUniformLocation('projection'); |
| 156 | + FUniformContainerNormalMatrix := FLightingShader.GetUniformLocation('normalMatrix'); |
| 157 | + |
| 158 | + { Define layout of the attributes in the Lighting shader } |
| 159 | + VertexLayout.Start(FLightingShader) |
| 160 | + .Add('position', 3) |
| 161 | + .Add('normal', 3) |
| 162 | + .Add('texCoords', 2); |
| 163 | + |
| 164 | + { Create the vertex array for the container. } |
| 165 | + FContainerVAO := TVertexArray.Create(VertexLayout, |
| 166 | + CONTAINER_VERTICES, SizeOf(CONTAINER_VERTICES), INDICES); |
| 167 | + |
| 168 | + { Load textures } |
| 169 | + |
| 170 | + { Diffuse Map |
| 171 | + =========== } |
| 172 | + glGenTextures(1, @FDiffuseMap); |
| 173 | + glBindTexture(GL_TEXTURE_2D, FDiffuseMap); |
| 174 | + |
| 175 | + { Set our texture parameters } |
| 176 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 177 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 178 | + |
| 179 | + { Set texture filtering } |
| 180 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 181 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 182 | + |
| 183 | + { Load, create texture and generate mipmaps } |
| 184 | + Data := TAssets.Load('textures/container2.png'); |
| 185 | + Assert(Assigned(Data)); |
| 186 | + Image := stbi_load_from_memory(Data, Length(Data), ImageWidth, ImageHeight, ImageComponents, 3); |
| 187 | + Assert(Assigned(Image)); |
| 188 | + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ImageWidth, ImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, Image); |
| 189 | + stbi_image_free(Image); |
| 190 | + glBindTexture(GL_TEXTURE_2D, 0); |
| 191 | + glErrorCheck; |
| 192 | + |
| 193 | + { Specular Map |
| 194 | + ============ } |
| 195 | + glGenTextures(1, @FSpecularMap); |
| 196 | + glBindTexture(GL_TEXTURE_2D, FSpecularMap); |
| 197 | + { All upcoming GL_TEXTURE_2D operations now have effect on our texture object } |
| 198 | + |
| 199 | + { Set our texture parameters } |
| 200 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 201 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 202 | + |
| 203 | + { Set texture filtering } |
| 204 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 205 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 206 | + |
| 207 | + { Load, create texture and generate mipmaps } |
| 208 | + Data := TAssets.Load('textures/container2_specular.png'); |
| 209 | + Assert(Assigned(Data)); |
| 210 | + Image := stbi_load_from_memory(Data, Length(Data), ImageWidth, ImageHeight, ImageComponents, 3); |
| 211 | + Assert(Assigned(Image)); |
| 212 | + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ImageWidth, ImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, Image); |
| 213 | + stbi_image_free(Image); |
| 214 | + glBindTexture(GL_TEXTURE_2D, 0); |
| 215 | + glErrorCheck; |
| 216 | + |
| 217 | + { Set texture units } |
| 218 | + FLightingShader.Use; |
| 219 | + glUniform1i(FLightingShader.GetUniformLocation('material.diffuse'), 0); |
| 220 | + glUniform1i(FLightingShader.GetUniformLocation('material.specular'), 1); |
| 221 | +end; |
| 222 | + |
| 223 | +procedure TLightCastersApp.KeyDown(const AKey: Integer; const AShift: TShiftState); |
| 224 | +begin |
| 225 | + if (AKey = vkEscape) then |
| 226 | + { Terminate app when Esc key is pressed } |
| 227 | + Terminate |
| 228 | + else |
| 229 | + FCamera.ProcessKeyDown(AKey); |
| 230 | +end; |
| 231 | + |
| 232 | +procedure TLightCastersApp.KeyUp(const AKey: Integer; const AShift: TShiftState); |
| 233 | +begin |
| 234 | + FCamera.ProcessKeyUp(AKey); |
| 235 | +end; |
| 236 | + |
| 237 | +procedure TLightCastersApp.MouseDown(const AButton: TMouseButton; |
| 238 | + const AShift: TShiftState; const AX, AY: Single); |
| 239 | +begin |
| 240 | + FCamera.ProcessMouseDown(AX, AY); |
| 241 | +end; |
| 242 | + |
| 243 | +procedure TLightCastersApp.MouseMove(const AShift: TShiftState; const AX, AY: Single); |
| 244 | +begin |
| 245 | + FCamera.ProcessMouseMove(AX, AY); |
| 246 | +end; |
| 247 | + |
| 248 | +procedure TLightCastersApp.MouseUp(const AButton: TMouseButton; |
| 249 | + const AShift: TShiftState; const AX, AY: Single); |
| 250 | +begin |
| 251 | + FCamera.ProcessMouseUp; |
| 252 | +end; |
| 253 | + |
| 254 | +procedure TLightCastersApp.MouseWheel(const AShift: TShiftState; |
| 255 | + const AWheelDelta: Integer); |
| 256 | +begin |
| 257 | + FCamera.ProcessMouseWheel(AWheelDelta); |
| 258 | +end; |
| 259 | + |
| 260 | +procedure TLightCastersApp.Resize(const AWidth, AHeight: Integer); |
| 261 | +begin |
| 262 | + inherited; |
| 263 | + if Assigned(FCamera) then |
| 264 | + FCamera.ViewResized(AWidth, AHeight); |
| 265 | +end; |
| 266 | + |
| 267 | +procedure TLightCastersApp.Shutdown; |
| 268 | +begin |
| 269 | + glDeleteTextures(1, @FDiffuseMap); |
| 270 | + glDeleteTextures(1, @FSpecularMap); |
| 271 | +end; |
| 272 | + |
| 273 | +procedure TLightCastersApp.Update(const ADeltaTimeSec, ATotalTimeSec: Double); |
| 274 | +var |
| 275 | + Model, View, Projection, Translate, Rotate: TMatrix4; |
| 276 | + NormalMatrix: TMatrix3; |
| 277 | + I: Integer; |
| 278 | +begin |
| 279 | + FCamera.HandleInput(ADeltaTimeSec); |
| 280 | + |
| 281 | + { Define the viewport dimensions } |
| 282 | + glViewport(0, 0, Width, Height); |
| 283 | + |
| 284 | + { Clear the color and depth buffer } |
| 285 | + glClearColor(0.1, 0.1, 0.1, 1.0); |
| 286 | + glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); |
| 287 | + |
| 288 | + { Use corresponding shader when setting uniforms/drawing objects } |
| 289 | + FLightingShader.Use; |
| 290 | + glUniform3f(FUniformLightPosition, FCamera.Position.X, FCamera.Position.Y, FCamera.Position.Z); |
| 291 | + glUniform3f(FUniformLightDirection, FCamera.Front.X, FCamera.Front.Y, FCamera.Front.Z); |
| 292 | + glUniform1f(FUniformLightCutOff, Cos(Radians(12.5))); |
| 293 | + glUniform1f(FUniformLightOuterCutOff, Cos(Radians(17.5))); |
| 294 | + glUniform3f(FUniformViewPos, FCamera.Position.X, FCamera.Position.Y, FCamera.Position.Z); |
| 295 | + |
| 296 | + { Set light properties } |
| 297 | + glUniform3f(FUniformLightAmbient, 0.1, 0.1, 0.1); |
| 298 | + { We set the diffuse intensity a bit higher; note that the right lighting |
| 299 | + conditions differ with each lighting method and environment. |
| 300 | + Each environment and lighting type requires some tweaking of these variables |
| 301 | + to get the best out of your environment. } |
| 302 | + glUniform3f(FUniformLightDiffuse, 0.8, 0.8, 0.8); |
| 303 | + glUniform3f(FUniformLightSpecular, 1.0, 1.0, 1.0); |
| 304 | + glUniform1f(FUniformLightConstant, 1.0); |
| 305 | + glUniform1f(FUniformLightLinear, 0.09); |
| 306 | + glUniform1f(FUniformLightQuadratic, 0.032); |
| 307 | + |
| 308 | + { Set material properties } |
| 309 | + glUniform1f(FUniformMaterialShininess, 32.0); |
| 310 | + |
| 311 | + { Create camera transformation } |
| 312 | + View := FCamera.GetViewMatrix; |
| 313 | + Projection.InitPerspectiveFovRH(Radians(FCamera.Zoom), Width / Height, 0.1, 100.0); |
| 314 | + |
| 315 | + { Pass matrices to shader } |
| 316 | + glUniformMatrix4fv(FUniformContainerView, 1, GL_FALSE, @View); |
| 317 | + glUniformMatrix4fv(FUniformContainerProjection, 1, GL_FALSE, @Projection); |
| 318 | + |
| 319 | + { Bind diffuse map } |
| 320 | + glActiveTexture(GL_TEXTURE0); |
| 321 | + glBindTexture(GL_TEXTURE_2D, FDiffuseMap); |
| 322 | + |
| 323 | + { Bind specular map } |
| 324 | + glActiveTexture(GL_TEXTURE1); |
| 325 | + glBindTexture(GL_TEXTURE_2D, FSpecularMap); |
| 326 | + |
| 327 | + { Draw 10 containers with the same VAO and VBO information; |
| 328 | + only their world space coordinates differ } |
| 329 | + FContainerVAO.BeginRender; |
| 330 | + for I := 0 to Length(CUBE_POSITIONS) - 1 do |
| 331 | + begin |
| 332 | + { Create Model matrix } |
| 333 | + Translate.InitTranslation(CUBE_POSITIONS[I]); |
| 334 | + Rotate.InitRotation(Vector3(1.0, 0.3, 0.5), Radians(20.0 * I)); |
| 335 | + Model := Rotate * Translate; |
| 336 | + |
| 337 | + { Calculate Normal matrix } |
| 338 | + NormalMatrix.Init(Model); |
| 339 | + NormalMatrix := NormalMatrix.Inverse.Transpose; |
| 340 | + |
| 341 | + { Draw the container } |
| 342 | + glUniformMatrix4fv(FUniformContainerModel, 1, GL_FALSE, @Model); |
| 343 | + glUniformMatrix3fv(FUniformContainerNormalMatrix, 1, GL_FALSE, @NormalMatrix); |
| 344 | + FContainerVAO.Render; |
| 345 | + end; |
| 346 | + FContainerVAO.EndRender; |
| 347 | +end; |
| 348 | + |
| 349 | +end. |
0 commit comments