diff --git a/examples/shaders/cel_shading/cel.fs b/examples/shaders/cel_shading/cel.fs new file mode 100644 index 00000000..78a2097c --- /dev/null +++ b/examples/shaders/cel_shading/cel.fs @@ -0,0 +1,60 @@ +#version 330 + +in vec3 fragPosition; +in vec2 fragTexCoord; +in vec4 fragColor; +in vec3 fragNormal; + +// Raylib standard uniforms +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// View position for future specular / fresnel use. +uniform vec3 viewPos; + +// Number of discrete toon bands (2 = hard binary, 10 = default, 20 = near-smooth). +uniform float numBands; + +// rlights.h compatible light block. +struct Light { + int enabled; + int type; // 0 = directional, 1 = point + vec3 position; + vec3 target; + vec4 color; + float attenuation; +}; +uniform Light lights[4]; + +out vec4 finalColor; + +void main() { + vec4 texColor = texture(texture0, fragTexCoord); + vec3 baseColor = texColor.rgb * fragColor.rgb * colDiffuse.rgb; + vec3 norm = normalize(fragNormal); + + float lightAccum = 0.08; // ambient floor + + for (int i = 0; i < 4; i++) { + if (lights[i].enabled == 0) continue; + + vec3 lightDir; + if (lights[i].type == 0) { + // Directional: direction is from position toward target. + lightDir = normalize(lights[i].position - lights[i].target); + } else { + // Point: direction from surface to light. + lightDir = normalize(lights[i].position - fragPosition); + } + + float NdotL = max(dot(norm, lightDir), 0.0); + + // Quantize NdotL into numBands discrete steps. + // min() guards against NdotL == 1.0 producing an out-of-range index. + float quantized = min(floor(NdotL * numBands), numBands - 1.0) / (numBands - 1.0); + lightAccum += quantized * lights[i].color.r; + } + + lightAccum = clamp(lightAccum, 0.0, 1.0); + finalColor = vec4(baseColor * lightAccum, texColor.a * colDiffuse.a); +} diff --git a/examples/shaders/cel_shading/cel.vs b/examples/shaders/cel_shading/cel.vs new file mode 100644 index 00000000..37a8d200 --- /dev/null +++ b/examples/shaders/cel_shading/cel.vs @@ -0,0 +1,25 @@ +#version 330 + +// Raylib standard attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; + +// Raylib standard uniforms +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; + +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec4 fragColor; +out vec3 fragNormal; + +void main() { + fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 0.0))); + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/cel_shading/light.go b/examples/shaders/cel_shading/light.go new file mode 100644 index 00000000..f74c56d6 --- /dev/null +++ b/examples/shaders/cel_shading/light.go @@ -0,0 +1,89 @@ +package main + +import ( + "fmt" + "unsafe" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +type LightType int32 + +const ( + LightTypeDirectional LightType = iota + LightTypePoint +) + +type Light struct { + Shader rl.Shader + LightType LightType + Position rl.Vector3 + Target rl.Vector3 + Color rl.Color + Enabled int32 + + // Shader location indexes + enabledLoc int32 + typeLoc int32 + posLoc int32 + targetLoc int32 + colorLoc int32 +} + +const maxLightsCount = 4 + +var lightCount = 0 + +// CreateLight initializes a light and sends initial values to the shader +func CreateLight( + lightType LightType, + position, target rl.Vector3, + color rl.Color, + shader rl.Shader, +) Light { + light := Light{ + Shader: shader, + } + + if lightCount < maxLightsCount { + light.Enabled = 1 + light.LightType = lightType + light.Position = position + light.Target = target + light.Color = color + + light.enabledLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].enabled", lightCount)) + light.typeLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].type", lightCount)) + light.posLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].position", lightCount)) + light.targetLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].target", lightCount)) + light.colorLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].color", lightCount)) + + light.UpdateValues() + lightCount++ + } + + return light +} + +// NewLight alias for CreateLight +func NewLight( + lightType LightType, + position, target rl.Vector3, + color rl.Color, + shader rl.Shader, +) Light { + return CreateLight(lightType, position, target, color, shader) +} + +// UpdateValues sends updated light attributes to the GLSL shader +func (lt *Light) UpdateValues() { + rl.SetShaderValue(lt.Shader, lt.enabledLoc, unsafe.Slice((*float32)(unsafe.Pointer(<.Enabled)), 1), rl.ShaderUniformInt) + rl.SetShaderValue(lt.Shader, lt.typeLoc, unsafe.Slice((*float32)(unsafe.Pointer(<.LightType)), 1), rl.ShaderUniformInt) + + rl.SetShaderValue(lt.Shader, lt.posLoc, []float32{lt.Position.X, lt.Position.Y, lt.Position.Z}, rl.ShaderUniformVec3) + rl.SetShaderValue(lt.Shader, lt.targetLoc, []float32{lt.Target.X, lt.Target.Y, lt.Target.Z}, rl.ShaderUniformVec3) + + rl.SetShaderValue(lt.Shader, lt.colorLoc, + []float32{float32(lt.Color.R) / 255.0, float32(lt.Color.G) / 255.0, float32(lt.Color.B) / 255.0, float32(lt.Color.A) / 255.0}, + rl.ShaderUniformVec4) +} diff --git a/examples/shaders/cel_shading/main.go b/examples/shaders/cel_shading/main.go new file mode 100644 index 00000000..0666368c --- /dev/null +++ b/examples/shaders/cel_shading/main.go @@ -0,0 +1,157 @@ +package main + +import ( + "fmt" + "math" + "unsafe" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +const ( + cullFaceFront = 0 // RL_CULL_FACE_FRONT + cullFaceBack = 1 // RL_CULL_FACE_BACK +) + +func main() { + const screenWidth = 800 + const screenHeight = 450 + + rl.SetConfigFlags(rl.FlagMsaa4xHint) + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - cel shading") + defer rl.CloseWindow() + + camera := rl.Camera3D{} + camera.Position = rl.NewVector3(9.0, 6.0, 9.0) + camera.Target = rl.NewVector3(0.0, 1.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) + camera.Fovy = 45.0 + camera.Projection = rl.CameraPerspective + + model := rl.LoadModel("old_car_new.glb") + defer rl.UnloadModel(model) + + materials := unsafe.Slice(model.Materials, model.MaterialCount) + + celShader := rl.LoadShader("cel.vs", "cel.fs") + defer rl.UnloadShader(celShader) + + celShaderLocs := unsafe.Slice(celShader.Locs, 32) + celShaderLocs[rl.ShaderLocVectorView] = rl.GetShaderLocation(celShader, "viewPos") + + defaultShader := materials[0].Shader + materials[0].Shader = celShader + + numBands := float32(10.0) + numBandsLoc := rl.GetShaderLocation(celShader, "numBands") + rl.SetShaderValue(celShader, numBandsLoc, []float32{numBands}, rl.ShaderUniformFloat) + + outlineShader := rl.LoadShader("outline_hull.vs", "outline_hull.fs") + defer rl.UnloadShader(outlineShader) + + outlineThicknessLoc := rl.GetShaderLocation(outlineShader, "outlineThickness") + + lights := make([]Light, maxLightsCount) + lights[0] = CreateLight(LightTypeDirectional, rl.NewVector3(50.0, 50.0, 50.0), rl.Vector3Zero(), rl.White, celShader) + + celEnabled := true + outlineEnabled := true + + rl.SetTargetFPS(60) + + for !rl.WindowShouldClose() { + rl.UpdateCamera(&camera, rl.CameraOrbital) + + cameraPos := []float32{camera.Position.X, camera.Position.Y, camera.Position.Z} + rl.SetShaderValue(celShader, celShaderLocs[rl.ShaderLocVectorView], cameraPos, rl.ShaderUniformVec3) + + if rl.IsKeyPressed(rl.KeyZ) { + celEnabled = !celEnabled + if celEnabled { + materials[0].Shader = celShader + } else { + materials[0].Shader = defaultShader + } + } + + if rl.IsKeyPressed(rl.KeyC) { + outlineEnabled = !outlineEnabled + } + + if rl.IsKeyPressed(rl.KeyE) || rl.IsKeyPressedRepeat(rl.KeyE) { + numBands = rl.Clamp(numBands+1.0, 2.0, 20.0) + } + if rl.IsKeyPressed(rl.KeyQ) || rl.IsKeyPressedRepeat(rl.KeyQ) { + numBands = rl.Clamp(numBands-1.0, 2.0, 20.0) + } + rl.SetShaderValue(celShader, numBandsLoc, []float32{numBands}, rl.ShaderUniformFloat) + + t := float32(rl.GetTime()) + lights[0].Position = rl.NewVector3( + float32(math.Sin(float64(-t*0.3)))*5.0, + 5.0, + float32(math.Cos(float64(-t*0.3)))*5.0, + ) + + for i := 0; i < maxLightsCount; i++ { + if lights[i].Enabled == 1 { + lights[i].UpdateValues() + } + } + + // Draw + rl.BeginDrawing() + + rl.ClearBackground(rl.RayWhite) + + rl.BeginMode3D(camera) + + if outlineEnabled { + + thickness := float32(0.005) + rl.SetShaderValue(outlineShader, outlineThicknessLoc, []float32{thickness}, rl.ShaderUniformFloat) + + rl.SetCullFace(cullFaceFront) + + materials[0].Shader = outlineShader + + rl.DrawModel(model, rl.Vector3Zero(), 0.75, rl.White) + + if celEnabled { + materials[0].Shader = celShader + } else { + materials[0].Shader = defaultShader + } + + rl.SetCullFace(cullFaceBack) + } + + rl.DrawModel(model, rl.Vector3Zero(), 0.75, rl.White) + rl.DrawSphereEx(lights[0].Position, 0.2, 50, 50, rl.Yellow) + rl.DrawGrid(10, 10.0) + + rl.EndMode3D() + + rl.DrawFPS(10, 10) + + celStatus := "OFF" + celColor := rl.DarkGray + if celEnabled { + celStatus = "ON" + celColor = rl.DarkGreen + } + rl.DrawText(fmt.Sprintf("Cel: %s [Z]", celStatus), 10, 65, 20, celColor) + + outlineStatus := "OFF" + outlineColor := rl.DarkGray + if outlineEnabled { + outlineStatus = "ON" + outlineColor = rl.DarkGreen + } + rl.DrawText(fmt.Sprintf("Outline: %s [C]", outlineStatus), 10, 90, 20, outlineColor) + + rl.DrawText(fmt.Sprintf("Bands: %.0f [Q/E]", numBands), 10, 115, 20, rl.DarkGray) + + rl.EndDrawing() + } +} diff --git a/examples/shaders/cel_shading/old_car_new.glb b/examples/shaders/cel_shading/old_car_new.glb new file mode 100644 index 00000000..119995ca Binary files /dev/null and b/examples/shaders/cel_shading/old_car_new.glb differ diff --git a/examples/shaders/cel_shading/outline_hull.fs b/examples/shaders/cel_shading/outline_hull.fs new file mode 100644 index 00000000..c8a33de0 --- /dev/null +++ b/examples/shaders/cel_shading/outline_hull.fs @@ -0,0 +1,7 @@ +#version 330 + +out vec4 finalColor; + +void main() { + finalColor = vec4(0.05, 0.05, 0.05, 1.0); +} diff --git a/examples/shaders/cel_shading/outline_hull.vs b/examples/shaders/cel_shading/outline_hull.vs new file mode 100644 index 00000000..2e350201 --- /dev/null +++ b/examples/shaders/cel_shading/outline_hull.vs @@ -0,0 +1,15 @@ +#version 330 + +in vec3 vertexPosition; +in vec3 vertexNormal; +in vec2 vertexTexCoord; +in vec4 vertexColor; + +uniform mat4 mvp; +uniform float outlineThickness; + +void main() { + // Extrude vertex along its normal to create the hull. + vec3 extruded = vertexPosition + vertexNormal * outlineThickness; + gl_Position = mvp * vec4(extruded, 1.0); +} diff --git a/examples/shaders/fog_rendering/fog.fs b/examples/shaders/fog_rendering/fog.fs new file mode 100644 index 00000000..ac0132ff --- /dev/null +++ b/examples/shaders/fog_rendering/fog.fs @@ -0,0 +1,92 @@ +#version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec4 fragColor; +in vec3 fragPosition; +in vec3 fragNormal; + +// Input uniform values +uniform sampler2D texture0; +uniform vec4 colDiffuse; + +// Output fragment color +out vec4 finalColor; + +// NOTE: Add your custom variables here + +#define MAX_LIGHTS 4 +#define LIGHT_DIRECTIONAL 0 +#define LIGHT_POINT 1 + +struct MaterialProperty { + vec3 color; + int useSampler; + sampler2D sampler; +}; + +struct Light { + int enabled; + int type; + vec3 position; + vec3 target; + vec4 color; +}; + +// Input lighting values +uniform Light lights[MAX_LIGHTS]; +uniform vec4 ambient; +uniform vec3 viewPos; +uniform vec4 fogColor; +uniform float fogDensity; + +void main() +{ + // Texel color fetching from texture sampler + vec4 texelColor = texture(texture0, fragTexCoord); + vec3 lightDot = vec3(0.0); + vec3 normal = normalize(fragNormal); + vec3 viewD = normalize(viewPos - fragPosition); + vec3 specular = vec3(0.0); + + // NOTE: Implement here your fragment shader code + + for (int i = 0; i < MAX_LIGHTS; i++) + { + if (lights[i].enabled == 1) + { + vec3 light = vec3(0.0); + + if (lights[i].type == LIGHT_DIRECTIONAL) light = -normalize(lights[i].target - lights[i].position); + if (lights[i].type == LIGHT_POINT) light = normalize(lights[i].position - fragPosition); + + float NdotL = max(dot(normal, light), 0.0); + lightDot += lights[i].color.rgb*NdotL; + + float specCo = 0.0; + if (NdotL > 0.0) specCo = pow(max(0.0, dot(viewD, reflect(-(light), normal))), 16.0); // Shine: 16.0 + specular += specCo; + } + } + + finalColor = (texelColor*((colDiffuse + vec4(specular,1))*vec4(lightDot, 1.0))); + finalColor += texelColor*(ambient/10.0); + + // Gamma correction + finalColor = pow(finalColor, vec4(1.0/2.2)); + + // Fog calculation + float dist = length(viewPos - fragPosition); + + // Exponential fog + float fogFactor = 1.0/exp((dist*fogDensity)*(dist*fogDensity)); + + // Linear fog (less nice) + //const float fogStart = 2.0; + //const float fogEnd = 10.0; + //float fogFactor = (fogEnd - dist)/(fogEnd - fogStart); + + fogFactor = clamp(fogFactor, 0.0, 1.0); + + finalColor = mix(fogColor, finalColor, fogFactor); +} diff --git a/examples/shaders/fog_rendering/light.go b/examples/shaders/fog_rendering/light.go new file mode 100644 index 00000000..b8f5a322 --- /dev/null +++ b/examples/shaders/fog_rendering/light.go @@ -0,0 +1,94 @@ +package main + +import ( + "fmt" + "unsafe" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +type LightType int32 + +const ( + LightTypeDirectional LightType = iota + LightTypePoint +) + +type Light struct { + Shader rl.Shader + LightType LightType + Position rl.Vector3 + Target rl.Vector3 + Color rl.Color + Enabled int32 + + // Shader location indexes + enabledLoc int32 + typeLoc int32 + posLoc int32 + targetLoc int32 + colorLoc int32 +} + +const maxLightsCount = 4 + +var lightCount = 0 + +// CreateLight initializes a light and sends initial values to the shader +func CreateLight( + lightType LightType, + position, target rl.Vector3, + color rl.Color, + shader rl.Shader, +) Light { + light := Light{ + Shader: shader, + } + + if lightCount < maxLightsCount { + light.Enabled = 1 + light.LightType = lightType + light.Position = position + light.Target = target + light.Color = color + + light.enabledLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].enabled", lightCount)) + light.typeLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].type", lightCount)) + light.posLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].position", lightCount)) + light.targetLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].target", lightCount)) + light.colorLoc = rl.GetShaderLocation(shader, fmt.Sprintf("lights[%d].color", lightCount)) + + light.UpdateValues() + lightCount++ + } + + return light +} + +// NewLight alias for CreateLight +func NewLight( + lightType LightType, + position, target rl.Vector3, + color rl.Color, + shader rl.Shader, +) Light { + return CreateLight(lightType, position, target, color, shader) +} + +// UpdateValues sends updated light attributes to the GLSL shader +func (lt *Light) UpdateValues() { + // Send to shader light enabled state and type (1 element slice instead of 4) + rl.SetShaderValue(lt.Shader, lt.enabledLoc, unsafe.Slice((*float32)(unsafe.Pointer(<.Enabled)), 1), rl.ShaderUniformInt) + rl.SetShaderValue(lt.Shader, lt.typeLoc, unsafe.Slice((*float32)(unsafe.Pointer(<.LightType)), 1), rl.ShaderUniformInt) + + // Send to shader light position values + rl.SetShaderValue(lt.Shader, lt.posLoc, []float32{lt.Position.X, lt.Position.Y, lt.Position.Z}, rl.ShaderUniformVec3) + + // Send to shader light target values + rl.SetShaderValue(lt.Shader, lt.targetLoc, []float32{lt.Target.X, lt.Target.Y, lt.Target.Z}, rl.ShaderUniformVec3) + + // Send to shader light color values + rl.SetShaderValue(lt.Shader, lt.colorLoc, + []float32{float32(lt.Color.R) / 255.0, float32(lt.Color.G) / 255.0, float32(lt.Color.B) / 255.0, float32(lt.Color.A) / 255.0}, + rl.ShaderUniformVec4) +} diff --git a/examples/shaders/fog_rendering/lighting.vs b/examples/shaders/fog_rendering/lighting.vs new file mode 100644 index 00000000..f8ec45f1 --- /dev/null +++ b/examples/shaders/fog_rendering/lighting.vs @@ -0,0 +1,32 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; + +// Output vertex attributes (to fragment shader) +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec4 fragColor; +out vec3 fragNormal; + +// NOTE: Add here your custom variables + +void main() +{ + // Send vertex attributes to fragment shader + fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0))); + + // Calculate final vertex position + gl_Position = mvp*vec4(vertexPosition, 1.0); +} diff --git a/examples/shaders/fog_rendering/main.go b/examples/shaders/fog_rendering/main.go new file mode 100644 index 00000000..d6f445ca --- /dev/null +++ b/examples/shaders/fog_rendering/main.go @@ -0,0 +1,117 @@ +package main + +import ( + "fmt" + "unsafe" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +func main() { + const screenWidth = 800 + const screenHeight = 450 + + rl.SetConfigFlags(rl.FlagMsaa4xHint) + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - fog rendering") + defer rl.CloseWindow() + + camera := rl.Camera3D{} + camera.Position = rl.NewVector3(2.0, 2.0, 6.0) + camera.Target = rl.NewVector3(0.0, 0.5, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) + camera.Fovy = 45.0 + camera.Projection = rl.CameraPerspective + + modelA := rl.LoadModelFromMesh(rl.GenMeshTorus(0.4, 1.0, 16, 32)) + modelB := rl.LoadModelFromMesh(rl.GenMeshCube(1.0, 1.0, 1.0)) + modelC := rl.LoadModelFromMesh(rl.GenMeshSphere(0.5, 32, 32)) + texture := rl.LoadTexture("texel_checker.png") + + materialsA := unsafe.Slice(modelA.Materials, modelA.MaterialCount) + materialsB := unsafe.Slice(modelB.Materials, modelB.MaterialCount) + materialsC := unsafe.Slice(modelC.Materials, modelC.MaterialCount) + + rl.SetMaterialTexture(&materialsA[0], rl.MapDiffuse, texture) + rl.SetMaterialTexture(&materialsB[0], rl.MapDiffuse, texture) + rl.SetMaterialTexture(&materialsC[0], rl.MapDiffuse, texture) + + shader := rl.LoadShader("lighting.vs", "fog.fs") + + shaderLocs := unsafe.Slice(shader.Locs, 32) + shaderLocs[rl.ShaderLocMatrixModel] = rl.GetShaderLocation(shader, "matModel") + shaderLocs[rl.ShaderLocVectorView] = rl.GetShaderLocation(shader, "viewPos") + + ambient := []float32{0.2, 0.2, 0.2, 1.0} + ambientLoc := rl.GetShaderLocation(shader, "ambient") + rl.SetShaderValue(shader, ambientLoc, ambient, rl.ShaderUniformVec4) + + fogColorNorm := rl.ColorNormalize(rl.Gray) + fogColor := []float32{fogColorNorm.X, fogColorNorm.Y, fogColorNorm.Z, fogColorNorm.W} + fogColorLoc := rl.GetShaderLocation(shader, "fogColor") + rl.SetShaderValue(shader, fogColorLoc, fogColor, rl.ShaderUniformVec4) + + fogDensity := float32(0.15) + fogDensityLoc := rl.GetShaderLocation(shader, "fogDensity") + rl.SetShaderValue(shader, fogDensityLoc, []float32{fogDensity}, rl.ShaderUniformFloat) + + materialsA[0].Shader = shader + materialsB[0].Shader = shader + materialsC[0].Shader = shader + + CreateLight(LightTypePoint, rl.NewVector3(0, 2, 6), rl.Vector3Zero(), rl.White, shader) + + rl.SetTargetFPS(60) + + for !rl.WindowShouldClose() { + + rl.UpdateCamera(&camera, rl.CameraOrbital) + + if rl.IsKeyDown(rl.KeyUp) { + fogDensity += 0.001 + if fogDensity > 1.0 { + fogDensity = 1.0 + } + } + + if rl.IsKeyDown(rl.KeyDown) { + fogDensity -= 0.001 + if fogDensity < 0.0 { + fogDensity = 0.0 + } + } + + rl.SetShaderValue(shader, fogDensityLoc, []float32{fogDensity}, rl.ShaderUniformFloat) + + modelA.Transform = rl.MatrixMultiply(modelA.Transform, rl.MatrixRotateX(-0.025)) + modelA.Transform = rl.MatrixMultiply(modelA.Transform, rl.MatrixRotateZ(0.012)) + + rl.SetShaderValue(shader, shaderLocs[rl.ShaderLocVectorView], []float32{camera.Position.X, camera.Position.Y, camera.Position.Z}, rl.ShaderUniformVec3) + + rl.BeginDrawing() + + rl.ClearBackground(rl.Gray) + + rl.BeginMode3D(camera) + + // Draw models + rl.DrawModel(modelA, rl.Vector3Zero(), 1.0, rl.White) + rl.DrawModel(modelB, rl.NewVector3(-2.6, 0, 0), 1.0, rl.White) + rl.DrawModel(modelC, rl.NewVector3(2.6, 0, 0), 1.0, rl.White) + + for i := -20; i < 20; i += 2 { + rl.DrawModel(modelA, rl.NewVector3(float32(i), 0, 2), 1.0, rl.White) + } + + rl.EndMode3D() + + rl.DrawText(fmt.Sprintf("Use KEY_UP/KEY_DOWN to change fog density [%.2f]", fogDensity), 10, 10, 20, rl.RayWhite) + + rl.EndDrawing() + } + + rl.UnloadModel(modelA) + rl.UnloadModel(modelB) + rl.UnloadModel(modelC) + rl.UnloadTexture(texture) + rl.UnloadShader(shader) +} diff --git a/examples/shaders/fog_rendering/texel_checker.png b/examples/shaders/fog_rendering/texel_checker.png new file mode 100644 index 00000000..79d3832a Binary files /dev/null and b/examples/shaders/fog_rendering/texel_checker.png differ