Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic lighting example #9

Merged
merged 2 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions examples/shaders/lighting.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#version 330

// Input vertex attributes (from vertex shader)
in vec3 fragPosition;
in vec2 fragTexCoord;
//in vec4 fragColor;
in vec3 fragNormal;

// Input uniform values
uniform sampler2D texture0;
uniform vec4 colDiffuse;

// Output fragment color
out vec4 finalColor;

// NOTE: Add here your custom variables

#define MAX_LIGHTS 4
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1

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;

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); // 16 refers to shine
specular += specCo;
}
}

finalColor = (texelColor*((colDiffuse + vec4(specular, 1.0))*vec4(lightDot, 1.0)));
finalColor += texelColor*(ambient/10.0)*colDiffuse;

// Gamma correction
finalColor = pow(finalColor, vec4(1.0/2.2));
}
32 changes: 32 additions & 0 deletions examples/shaders/lighting.vs
Original file line number Diff line number Diff line change
@@ -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);
}
188 changes: 188 additions & 0 deletions examples/shaders_basic_lighting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
use raylib_ffi::*;
use ::std::os::raw::*;

const MAX_LIGHTS: usize = 4; // Max dynamic lights supported by shader

// Light data
struct Light {
enabled: i32,
kind: i32,
position: Vector3,
target: Vector3,
color: Color,

// Shader locations
enabled_loc: i32,
kind_loc: i32,
position_loc: i32,
target_loc: i32,
color_loc: i32,
}

// Create a light and get shader locations
fn create_light(id: isize, kind: i32, position: Vector3, target : Vector3, color : Color, shader : Shader) -> Light {
unsafe {
let light = Light {
enabled: 1,
kind,
position,
target,
color,

// NOTE: Lighting shader naming must be the provided ones
enabled_loc: GetShaderLocation(shader, rl_str!(format!("lights[{}].enabled", id))),
kind_loc: GetShaderLocation(shader, rl_str!(format!("lights[{}].type", id))),
position_loc: GetShaderLocation(shader, rl_str!(format!("lights[{}].position", id))),
target_loc: GetShaderLocation(shader, rl_str!(format!("lights[{}].target", id))),
color_loc: GetShaderLocation(shader, rl_str!(format!("lights[{}].color", id)))
};

update_light_values(shader, &light);
return light;
}
}

// Send light properties to shader
// NOTE: Light shader locations should be available
fn update_light_values(shader: Shader, light: &Light) {
unsafe {
// Send to shader light enabled state and type
let enabled = [light.enabled].as_ptr();
SetShaderValue(shader, light.enabled_loc, enabled as *const c_void, enums::ShaderUniformDataType::Int as i32);
let kind = [light.kind].as_ptr();
SetShaderValue(shader, light.kind_loc, kind as *const c_void, enums::ShaderUniformDataType::Int as i32);

// Send to shader light position values
let position = [light.position.x, light.position.y, light.position.z].as_ptr();
SetShaderValue(shader, light.position_loc, position as *const c_void, enums::ShaderUniformDataType::Vec3 as i32);

// Send to shader light target position values
let target = [light.position.x, light.position.y, light.position.z].as_ptr();
SetShaderValue(shader, light.target_loc, target as *const c_void, enums::ShaderUniformDataType::Vec3 as i32);

// Send to shader light color values
let color = [
light.color.r as f32 / 255.0,
light.color.g as f32 / 255.0,
light.color.b as f32 / 255.0,
light.color.a as f32 / 255.0
].as_ptr();
SetShaderValue(shader, light.color_loc, color as *const c_void, enums::ShaderUniformDataType::Vec4 as i32);
}
}

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
pub fn main() {
unsafe {
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(enums::ConfigFlags::Msaa4xHint as u32); // Enable Multi Sampling Anti Aliasing 4x (if available)
InitWindow(800, 450, rl_str!("raylib [shaders] example - basic lighting"));

// Define the camera to look into our 3d world
let mut camera = Camera{
position: Vector3{ x: 2.0, y: 4.0, z: 6.0 }, // Camera position
target: Vector3{ x: 0.0, y: 0.5, z: 0.0 }, // Camera looking at point
up: Vector3{ x: 0.0, y: 1.0, z: 0.0 }, // Camera up vector (rotation towards target)
fovy: 45.0, // Camera field-of-view Y
projection: enums::CameraProjection::Perspective as i32 // Camera projection type
};

// Load plane model from a generated mesh
let model = LoadModelFromMesh(GenMeshPlane(10.0, 10.0, 3, 3));
let cube = LoadModelFromMesh(GenMeshCube(2.0, 4.0, 2.0));

// Load basic lighting shader
let shader = LoadShader(rl_str!("examples/shaders/lighting.vs"), rl_str!("examples/shaders/lighting.fs"));

// Get some required shader locations
let view_loc = shader.locs.offset(enums::ShaderLocationIndex::VectorView as isize) as *mut c_int;
*view_loc = GetShaderLocation(shader, rl_str!("viewPos"));

// Ambient light level (some basic lighting)
let ambient_loc = GetShaderLocation(shader, rl_str!("ambient"));
let ambient_value = [0.1 as f32, 0.1 as f32, 0.1 as f32, 1.0 as f32].as_ptr();
SetShaderValue(shader, ambient_loc, ambient_value as *const c_void, enums::ShaderUniformDataType::Ivec4 as i32);

// Assign lighting shader to model
(*(model.materials.offset(0))).shader = shader;
(*(cube.materials.offset(0))).shader = shader;

// Create lights
let mut lights = [
create_light(0, 1, Vector3{ x: -2.0, y: 1.0, z: -2.0 }, Vector3{ x: 0.0, y: 0.0, z: 0.0 }, colors::YELLOW, shader),
create_light(1, 1, Vector3{ x: 2.0, y: 1.0, z: 2.0 }, Vector3{ x: 0.0, y: 0.0, z: 0.0 }, colors::RED, shader),
create_light(2, 1, Vector3{ x: -2.0, y: 1.0, z: 2.0 }, Vector3{ x: 0.0, y: 0.0, z: 0.0 }, colors::GREEN, shader),
create_light(3, 1, Vector3{ x: 2.0, y: 1.0, z: -2.0 }, Vector3{ x: 0.0, y: 0.0, z: 0.0 }, colors::BLUE, shader)
];

SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------

// Main game loop
while !WindowShouldClose() // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&mut camera, enums::CameraMode::Orbital as i32);

// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
let camera_pos = [camera.position.x, camera.position.y, camera.position.z].as_ptr();
SetShaderValue(shader, shader.locs.offset(enums::ShaderLocationIndex::VectorView as isize).read(), camera_pos as *mut c_void, enums::ShaderUniformDataType::Ivec3 as c_int);

// Check key inputs to enable/disable lights
if IsKeyPressed(enums::KeyboardKey::R as i32) { lights[1].enabled = !lights[1].enabled; }
if IsKeyPressed(enums::KeyboardKey::G as i32) { lights[2].enabled = !lights[2].enabled; }
if IsKeyPressed(enums::KeyboardKey::B as i32) { lights[3].enabled = !lights[3].enabled; }
if IsKeyPressed(enums::KeyboardKey::Y as i32) { lights[0].enabled = !lights[0].enabled; }

// Update light values (actually, only enable/disable them)
for i in 0 .. MAX_LIGHTS {
update_light_values(shader, &lights[i]);
}
//----------------------------------------------------------------------------------

// Draw
//----------------------------------------------------------------------------------
BeginDrawing();

ClearBackground(colors::WHITE);

BeginMode3D(camera);

DrawModel(model, Vector3{ x: 0.0, y: 0.0, z: 0.0 }, 1.0, colors::WHITE);
DrawModel(cube, Vector3{ x: 0.0, y: 0.0, z: 0.0 }, 1.0, colors::WHITE);

// Draw spheres to show where the lights are
for i in 0 .. MAX_LIGHTS {
if lights[i].enabled > 0 {
DrawSphereEx(lights[i].position, 0.2, 8, 8, lights[i].color);
} else {
DrawSphereWires(lights[i].position, 0.2, 8, 8, ColorAlpha(lights[i].color, 0.3));
}
}

DrawGrid(10, 1.0);

EndMode3D();

DrawFPS(10, 10);

DrawText(rl_str!("Use keys [Y][R][G][B] to toggle lights"), 10, 40, 20, colors::DARKGRAY);

EndDrawing();
//----------------------------------------------------------------------------------
}

// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model); // Unload the model
UnloadModel(cube); // Unload the model
UnloadShader(shader); // Unload shader

CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
}
Loading