Finished lighting shaders

This commit is contained in:
zyb3rwolfi 2025-11-25 14:46:16 +00:00
parent 4de01aa53b
commit fe63a6d938
11 changed files with 258 additions and 48 deletions

View File

@ -6,6 +6,8 @@
<Path>TheLabs/Textures</Path>
</attachedFolders>
<explicitIncludes />
<explicitExcludes />
<explicitExcludes>
<Path>TheLabs/Shapes/Bus.cs</Path>
</explicitExcludes>
</component>
</project>

View File

@ -0,0 +1,29 @@
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
namespace TheLabs;
public struct Lighting
{
public Vector3 position;
public Vector3 ambientColour;
public Vector3 diffuseColour;
public Vector3 specularColour;
public Lighting(Vector3 pos, Vector3 ambient, Vector3 diffuse, Vector3 specular)
{
position = pos;
ambientColour = ambient;
diffuseColour = diffuse;
specularColour = specular;
}
public void Apply(Shader shader)
{
GL.Uniform3(GL.GetUniformLocation(shader.Handle, "uPointLight.position"), ref position);
GL.Uniform3(GL.GetUniformLocation(shader.Handle, "uPointLight.ambientColour"), ref ambientColour);
GL.Uniform3(GL.GetUniformLocation(shader.Handle, "uPointLight.diffuseColour"), ref diffuseColour);
GL.Uniform3(GL.GetUniformLocation(shader.Handle, "uPointLight.specularColour"), ref specularColour);
}
}

View File

@ -0,0 +1,30 @@
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
namespace TheLabs;
public struct Material
{
public Vector3 ambientColour;
public Vector3 diffuseColour;
public Vector3 specularColour;
public float shininess;
public Material(Vector3 ambient, Vector3 diffuse, Vector3 specular, float shininessFactor)
{
ambientColour = ambient;
diffuseColour = diffuse;
specularColour = specular;
shininess = shininessFactor;
}
public void Apply(Shader shader)
{
GL.Uniform3(GL.GetUniformLocation(shader.Handle, "uMaterial.ambientColour"), ref ambientColour);
GL.Uniform3(GL.GetUniformLocation(shader.Handle, "uMaterial.diffuseColour"), ref diffuseColour);
GL.Uniform3(GL.GetUniformLocation(shader.Handle, "uMaterial.specularColour"), ref specularColour);
GL.Uniform1(GL.GetUniformLocation(shader.Handle, "uMaterial.shininess"), shininess);
}
}

View File

@ -11,39 +11,37 @@ namespace TheLabs
{
public class MyExampleWindow : GameWindow
{
// Create the vertices for our triangle. These are listed in normalized device coordinates (NDC)
// In NDC, (0, 0) is the center of the screen.
// Negative X coordinates move to the left, positive X move to the right.
// Negative Y coordinates move to the bottom, positive Y move to the top.
// OpenGL only supports rendering in 3D, so to create a flat triangle, the Z coordinate will be kept as 0.
// -- Scene Graph Nodes
private SceneNode _rootNode;
private SceneNode _characterNode;
private SceneNode _buildingNode;
// -- OpenGL Resources
private Shader _shader;
private Camera _camera;
// - Resources that only need to be created once
private Mesh _exampleObject;
private Mesh _buildingObject;
private Texture _texture;
private RenderObject _render;
private RenderObject _buildingRender;
float _rotation = 0.0f;
// -- Scene Objects
// Camera parameters
private Vector3 _cameraPosition = new Vector3(0.0f, 0.0f, -3.0f);
private Vector3 _cameraFront = new Vector3(0.0f, 0.0f, -1.0f);
private Vector3 _cameraUp = Vector3.UnitY;
private float _yaw = -90.0f;
private float _pitch = 0.0f;
private float _cameraSpeed = 2.5f;
private float _rotationSpeed = 50.0f;
private float _mouseSensitivity = 0.1f;
private float _pitch = 0.0f;
private float _rotation = 0.0f;
// Materials and Lighting
private Lighting _mainLight;
private Material _defaultMaterial;
// This prevents a large camera jump when the window first gets focus
private bool _firstMove = true;
@ -58,24 +56,42 @@ namespace TheLabs
{
// This is called when the window is created and is where we can set up OpenGL resources.
base.OnLoad();
//GL.Disable(EnableCap.CullFace);
//GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
// --- Set up Lighting and Material ---
_mainLight = new Lighting(new Vector3(1.2f, 1.0f, 2.0f),
new Vector3(0.3f),
new Vector3(1.5f),
new Vector3(1.0f));
_defaultMaterial = new Material(
new Vector3(0.5f, 0.5f, 0.5f) * 0.1f,
new Vector3(0.5f, 0.5f, 0.5f) * 0.6f,
new Vector3(1.0f, 1.0f, 1.0f),
32.0f);
// --- Set up OpenGL State ---
GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);
GL.Enable(EnableCap.DepthTest);
// --- Set up Camera ---
_camera = new Camera(_cameraPosition, _cameraFront, _cameraUp);
// -- Load Shaders, Textures, and Create Scene Objects ---
_shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
_texture = new Texture("Textures/placeholder.png");
// --- Create Scene Graph ---
_rootNode = new SceneNode();
_buildingObject = ShapeFactory.CreateTexturedCube();
_buildingRender = new RenderObject(_buildingObject, _shader, _texture);
// Create Example Object
_buildingObject = ShapeFactory.CreateTexturedSphere();
_buildingRender = new RenderObject(_buildingObject, _shader, _texture, _defaultMaterial, _mainLight);
_buildingNode = new SceneNode(_buildingRender);
_buildingNode.Position = new Vector3(2.0f, -1.0f, 0.0f);
_buildingNode.Scale = new Vector3(0.1f, 0.1f, 0.1f);
_rootNode.AddChild(_buildingNode);
// Create Character Object
CursorState = CursorState.Grabbed;
@ -89,8 +105,6 @@ namespace TheLabs
base.OnRenderFrame(e);
// Clear the color buffer and the depth buffer
GL.Clear(ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit);
//_cubeObject.Rotation = Quaternion.FromEulerAngles(_rotation * 0.5f, _rotation, 0); //
//_cubeObject2.Rotation = Quaternion.FromEulerAngles(-_rotation * 0.5f, -_rotation, 0);
// --- Set up Camera ---
Matrix4 view = _camera.LookAt();
@ -101,15 +115,17 @@ namespace TheLabs
{
aspectRatio = 1.0f; // Default to 1:1 if window is minimized
}
// 3. Create the projection matrix
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(
MathHelper.DegreesToRadians(45f),
aspectRatio,
0.1f,
100.0f
);
// --- Draw Scene Objects ---
_rootNode.Draw(view, projection, Matrix4.Identity);
// --- Draw Scene Objects ---
_rootNode.Draw(view, projection, Matrix4.Identity, _mainLight);
SwapBuffers();
}

View File

@ -9,17 +9,21 @@ public class RenderObject
public Mesh Mesh;
public Shader Shader;
public Texture Texture;
public Material Material;
public Lighting Lighting;
public Vector3 Position = Vector3.Zero;
public Matrix4 Transform = Matrix4.Identity;
public Quaternion Rotation = Quaternion.Identity;
public Vector3 Scale = Vector3.One;
public RenderObject(Mesh mesh, Shader shader, Texture texture)
public RenderObject(Mesh mesh, Shader shader, Texture texture, Material material, Lighting lighting)
{
Mesh = mesh;
Shader = shader;
Texture = texture;
Material = material;
Lighting = lighting;
}
public RenderObject(Mesh mesh, Shader shader)
@ -28,14 +32,20 @@ public class RenderObject
Shader = shader;
}
public void Draw(Matrix4 view, Matrix4 projection)
public void Draw(Matrix4 view, Matrix4 projection, Lighting light)
{
// 1. Activate Shader
Vector3 pink = new Vector3(0.5f, 0.5f, 0.5f);
Material mat = new Material(pink * 0.1f, pink * 0.6f, Vector3.One, 32.0f);
Shader.Use();
// 2. Bind Texture
Texture.Use();
Shader.SetInt("uTexture", 0);
// 3. Calculate Model Matrix
Material.Apply(Shader);
light.Apply(Shader);
Vector3 cameraPos = view.Inverted().ExtractTranslation();
Shader.SetVector3("uViewPos", cameraPos);
Matrix4 model = Matrix4.CreateScale(Scale) * Matrix4.CreateFromQuaternion(Rotation) * Matrix4.CreateTranslation(Position);
int modelLoc = GL.GetUniformLocation(Shader.Handle, "model");
@ -47,7 +57,9 @@ public class RenderObject
int projLoc = GL.GetUniformLocation(Shader.Handle, "projection");
GL.UniformMatrix4(projLoc, false, ref projection); // Try TRUE here
// 5. Tell the Mesh to draw itself
Mesh.Draw();
}
}

View File

@ -23,7 +23,7 @@ public class SceneNode
Children.Add(child);
}
public void Draw(Matrix4 view, Matrix4 projection, Matrix4 parentTransform)
public void Draw(Matrix4 view, Matrix4 projection, Matrix4 parentTransform, Lighting light)
{
Matrix4 localTransform = Matrix4.CreateScale(Scale) * Matrix4.CreateFromQuaternion(Rotation) * Matrix4.CreateTranslation(Position);
Matrix4 globalTransform = localTransform * parentTransform;
@ -34,12 +34,12 @@ public class SceneNode
RenderObject.Rotation = Quaternion.Identity;
RenderObject.Scale = Vector3.One;
RenderObject.Draw(view, projection);
RenderObject.Draw(view, projection, light);
}
foreach (var child in Children)
{
child.Draw(view, projection, globalTransform);
child.Draw(view, projection, globalTransform, light);
}
}
}

View File

@ -1,37 +1,81 @@
#version 330 core
out vec4 outputColor;
// Fragment shader for basic lighting with texture mapping
out vec4 outputColor; // The final output color of the fragment
// --- INPUTS ---
// These come from the Vertex Shader
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;
struct MaterialProperty {
vec3 ambientColour;
vec3 diffuseColour;
vec3 specularColour;
float shininess;
};
struct LightProperty {
vec3 position;
vec3 ambientColour;
vec3 diffuseColour;
vec3 specularColour;
};
// --- UNIFORMS ---
uniform sampler2D uTexture;
uniform sampler2D uTexture; // Image Texture
uniform vec3 uViewPos; // Camera Position
uniform MaterialProperty uMaterial; // Material Properties
uniform LightProperty uPointLight; // Light Properties
void main()
{
// Lighting Setup
// --- LIGHTING CALCULATIONS ---
// Lighting Setup (hardcoded for simplicity)
vec3 lightPos = vec3(1.2f, 1.0f, 2.0f);
vec3 lightColor = vec3(1.0f, 1.0f, 1.0f);
// 1. Get Texture Color (Keep it as vec4!)
// 1. Get Texture Color
// Sample the texture at the given texture coordinates
vec4 objColor = texture(uTexture, TexCoord);
// 2. Ambient
// 2. Ambient strength
// A constant, dim light that ensure s object are not completely dark
// It simply multiplies the light color by a small factor
float ambientStrength = 0.1f;
vec3 ambient = ambientStrength * lightColor;
vec3 ambient = uMaterial.ambientColour * uPointLight.ambientColour;
// 3. Diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0f);
vec3 diffuse = diff * lightColor;
// Creates the "matte" effect by calculating the angle between the light direction and the surface normal
// 4. Combine
// Note: We use objColor.rgb (vec3) to match the lighting calculation
vec3 result = (ambient + diffuse) * objColor.rgb;
// Normalize the normal vector and calculate the light direction
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(uPointLight.position - FragPos);
// The dot product gives us the cosine of the angle between the two vectors
// The max function ensures we don't get negative values (light coming from behind the surface)
float diff = max(dot(norm, lightDir), 0.0f);
vec3 diffuse = (uPointLight.diffuseColour * diff) * uMaterial.diffuseColour;
// 4. Specular
// Creates the shiny highlights on the surface
float shininess = 32.0f;
// Calculate the view direction and the reflection direction
vec3 viewDirection = normalize(uViewPos - FragPos);
// If the light is coming from the light direction, we need to negate it for reflection
vec3 reflectDirection = reflect(-lightDir, normalize(Normal));
// Calculate the specular component using the dot product and shininess factor
float spec = pow(max(dot(viewDirection, reflectDirection), 0.0f), shininess);
vec3 specular = uMaterial.specularColour * spec * uPointLight.specularColour;
// 5. Combine
// We multiply (Ambient + Diffuse) by the Texture Color.
// We ADD Specular afterwards so the highlights look bright white (like plastic/metal).
vec3 result = (ambient + diffuse) * objColor.rgb + specular;
outputColor = vec4(result, objColor.a);
}

View File

@ -1,16 +1,21 @@
#version 330 core
// Vertex shader decides where each vertex is in 3D space
// --- INPUTS ---
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTexCoord;
// Comes directly from the vertex buffer
layout(location = 0) in vec3 aPosition; // Raw 3D position
layout(location = 1) in vec3 aNormal; // The direction perpendicular to the surface
layout(location = 2) in vec2 aTexCoord; // The U,V texture coordinates
// --- OUTPUTS ---
// We can pass data to the fragment shader through these
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoord;
// --- UNIFORMS ---
// These are matrices that help transform our vertices
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

View File

@ -253,6 +253,74 @@ public static class ShapeFactory
// --- 4. Create the Mesh ---
return new Mesh(vertices.ToArray(), indices.ToArray(), Mesh.VertexLayout.PosTex);
}
public static Mesh CreateTexturedSphere(float radius = 0.5f, int stacks = 20, int slices = 20)
{
var vertices = new List<float>();
var indices = new List<uint>();
// 1. Generate Vertices
for (int i = 0; i <= stacks; i++)
{
// V ranges from 0.0 to 1.0 (bottom to top)
float v = (float)i / stacks;
// Phi ranges from -PI/2 (bottom) to +PI/2 (top)
float phi = v * MathF.PI - MathF.PI / 2;
for (int j = 0; j <= slices; j++)
{
// U ranges from 0.0 to 1.0 (around the sphere)
float u = (float)j / slices;
// Theta ranges from 0 to 2*PI
float theta = u * 2 * MathF.PI;
// Calculate Position (Spherical Coordinates)
float x = radius * MathF.Cos(phi) * MathF.Cos(theta);
float y = radius * MathF.Sin(phi);
float z = radius * MathF.Cos(phi) * MathF.Sin(theta);
// Calculate Normal (Normalized position)
// Since center is (0,0,0), normal is just (x,y,z) normalized.
// Or simpler: (x/r, y/r, z/r)
float nx = x / radius;
float ny = y / radius;
float nz = z / radius;
// Add Vertex Data: Pos (3) + Normal (3) + Tex (2)
vertices.Add(x); vertices.Add(y); vertices.Add(z); // Position
vertices.Add(nx); vertices.Add(ny); vertices.Add(nz); // Normal
vertices.Add(u); vertices.Add(v); // Texture
}
}
// 2. Generate Indices
for (int i = 0; i < stacks; i++)
{
for (int j = 0; j < slices; j++)
{
// We are building a quad from 4 vertices:
// TopLeft (TL), TopRight (TR), BottomLeft (BL), BottomRight (BR)
uint bl = (uint)(i * (slices + 1) + j);
uint br = (uint)(i * (slices + 1) + (j + 1));
uint tl = (uint)((i + 1) * (slices + 1) + j);
uint tr = (uint)((i + 1) * (slices + 1) + (j + 1));
// Triangle 1
indices.Add(bl);
indices.Add(br);
indices.Add(tl);
// Triangle 2
indices.Add(br);
indices.Add(tr);
indices.Add(tl);
}
}
return new Mesh(vertices.ToArray(), indices.ToArray(), Mesh.VertexLayout.PosTexNormal);
}
}

View File

@ -52,7 +52,7 @@ public class Bus
foreach (var pos in wheelPositions)
{
var wheelObj = new RenderObject(_wheelMesh, shader, wheelTexture);
//var wheelObj = new RenderObject(_wheelMesh, shader, wheelTexture);
var wheelNode = new SceneNode(wheelObj);
wheelNode.Position = pos; // Relative to the MainNode!
@ -68,7 +68,7 @@ public class Bus
public void Draw(Matrix4 view, Matrix4 projection)
{
MainNode.Draw(view, projection, Matrix4.Identity);
//MainNode.Draw(view, projection, Matrix4.Identity);
}

View File

@ -30,4 +30,8 @@
<None Include="..\..\README.md" Link="README.md" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Shapes\Bus.cs" />
</ItemGroup>
</Project>