r/opengl
[HELP] Spotlight doesn't work
version 330 core
out vec4 FragColor; in vec2 TexCoo; in vec3 FragPos; in vec3 Normal;
struct DirLight { vec3 direction; vec3 ambient; vec3 diffuse; vec3 specular; }; struct PointLight { vec3 position; vec3 ambient; vec3 diffuse; vec3 specular;
float constant;
float linear;
float quadratic;
};
struct Materijal { sampler2D diffuse; sampler2D specular; float shininess; };
struct SpotLight { vec3 position; float cutOff; float outcutOff; vec3 direction;
float constant;
float linear;
float quadratic;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
#define NR_POINT_LIGHTS 4 uniform SpotLight spotlight; uniform PointLight pointLights[NR_POINT_LIGHTS]; uniform DirLight dirLight; uniform Materijal materijal; uniform vec3 viewPos; vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 viewDir); vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir); vec3 CalcPointLight(PointLight light, vec3 normal, vec3 viewDir); void main() { vec3 norm = normalize(Normal); vec3 viewDir = normalize(viewPos - FragPos); vec3 result = CalcDirLight(dirLight, norm, viewDir); for(int i =0;i<NR_POINT_LIGHTS;i++) result += CalcPointLight(pointLights[i], norm, viewDir); result += CalcSpotLight(spotlight,norm,viewDir); FragColor = vec4(result,1.0); } vec3 CalcDirLight(DirLight light, vec3 normal,vec3 viewDir) { vec3 lightDir = normalize(-light.direction); float diff = max(dot(lightDir, normal),0.0); vec3 reflectDir= reflect(-lightDir, normal); float spec = pow(max(0.0, dot(reflectDir, viewDir)), materijal.shininess); vec3 ambient = light.ambient * vec3(texture(materijal.diffuse, TexCoo)); vec3 diffuse = light.diffuse * diff * vec3(texture(materijal.diffuse, TexCoo)); vec3 specular = light.specular * spec * vec3(texture(materijal.specular,TexCoo)); return (ambient + specular + diffuse); } vec3 CalcPointLight(PointLight light, vec3 normal, vec3 viewDir) { vec3 lightDir = normalize(light.position - FragPos); float diff = max(dot(lightDir, normal),0.0); vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(viewDir, reflectDir),0.0),materijal.shininess); float distance = length(light.position - FragPos); float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * distance * distance); vec3 ambient = light.ambient * vec3(texture(materijal.diffuse, TexCoo)); vec3 diffuse = light.diffuse * diff * vec3(texture(materijal.diffuse, TexCoo)); vec3 specular = light.specular * spec * vec3(texture(materijal.specular,TexCoo)); ambient*= attenuation; diffuse *= attenuation; specular = attenuation; return (ambient + diffuse + specular); } vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 viewDir) { vec3 lightDir = normalize(light.position - FragPos); float diff = max(dot(normal,lightDir),0.0); vec3 reflectDir = reflect(-lightDir, normal); float spec = pow(max(dot(reflectDir, viewDir),0.0),materijal.shininess); float distance = length(light.position - FragPos); float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * distance * distance); float theta = dot(normalize(-light.direction), lightDir); float epsilon = light.cutOff - light.outcutOff; float intensity = clamp((theta - light.outcutOff) / epsilon, 0.0, 1.0); vec3 ambient = light.ambient * vec3(texture(materijal.diffuse, TexCoo)); vec3 diffuse = light.diffuse * diff * vec3(texture(materijal.diffuse, TexCoo)); vec3 specular = light.specular * spec * vec3(texture(materijal.specular,TexCoo)); ambient= attenuation * intensity; diffuse *= attenuation * intensity; specular *= attenuation * intensity; return (ambient + diffuse + specular);
}
this is the main function inside the while (!glfwWindowShouldClose(window)) among other standard stuff /* Here we set all the uniforms for the 5/6 types of lights we have. We have to set them manually and index the proper PointLight struct in the array to set each uniform variable. This can be done more code-friendly by defining light types as classes and set their values in there, or by using a more efficient uniform approach by using 'Uniform buffer objects', but that is something we'll discuss in the 'Advanced GLSL' tutorial. */ // directional light lightingShader.setVec3("dirLight.direction", -0.2f, -1.0f, -0.3f); lightingShader.setVec3("dirLight.ambient", 0.05f, 0.05f, 0.05f); lightingShader.setVec3("dirLight.diffuse", 0.4f, 0.4f, 0.4f); lightingShader.setVec3("dirLight.specular", 0.5f, 0.5f, 0.5f); // point light 1 lightingShader.setVec3("pointLights[0].position", pointLightPositions[0]); lightingShader.setVec3("pointLights[0].ambient", 0.05f, 0.05f, 0.05f); lightingShader.setVec3("pointLights[0].diffuse", 0.8f, 0.8f, 0.8f); lightingShader.setVec3("pointLights[0].specular", 1.0f, 1.0f, 1.0f); lightingShader.setFloat("pointLights[0].constant", 1.0f); lightingShader.setFloat("pointLights[0].linear", 0.09); lightingShader.setFloat("pointLights[0].quadratic", 0.032); // point light 2 lightingShader.setVec3("pointLights[1].position", pointLightPositions[1]); lightingShader.setVec3("pointLights[1].ambient", 0.05f, 0.05f, 0.05f); lightingShader.setVec3("pointLights[1].diffuse", 0.8f, 0.8f, 0.8f); lightingShader.setVec3("pointLights[1].specular", 1.0f, 1.0f, 1.0f); lightingShader.setFloat("pointLights[1].constant", 1.0f); lightingShader.setFloat("pointLights[1].linear", 0.09); lightingShader.setFloat("pointLights[1].quadratic", 0.032); // point light 3 lightingShader.setVec3("pointLights[2].position", pointLightPositions[2]); lightingShader.setVec3("pointLights[2].ambient", 0.05f, 0.05f, 0.05f); lightingShader.setVec3("pointLights[2].diffuse", 0.8f, 0.8f, 0.8f); lightingShader.setVec3("pointLights[2].specular", 1.0f, 1.0f, 1.0f); lightingShader.setFloat("pointLights[2].constant", 1.0f); lightingShader.setFloat("pointLights[2].linear", 0.09); lightingShader.setFloat("pointLights[2].quadratic", 0.032); // point light 4 lightingShader.setVec3("pointLights[3].position", pointLightPositions[3]); lightingShader.setVec3("pointLights[3].ambient", 0.05f, 0.05f, 0.05f); lightingShader.setVec3("pointLights[3].diffuse", 0.8f, 0.8f, 0.8f); lightingShader.setVec3("pointLights[3].specular", 1.0f, 1.0f, 1.0f); lightingShader.setFloat("pointLights[3].constant", 1.0f); lightingShader.setFloat("pointLights[3].linear", 0.09); lightingShader.setFloat("pointLights[3].quadratic", 0.032); // spotLight lightingShader.setVec3("spotLight.position", camera.Position); lightingShader.setVec3("spotLight.direction", camera.Front); lightingShader.setVec3("spotLight.ambient", 0.0f, 0.0f, 0.0f); lightingShader.setVec3("spotLight.diffuse", 1.0f, 1.0f, 1.0f); lightingShader.setVec3("spotLight.specular", 1.0f, 1.0f, 1.0f); lightingShader.setFloat("spotLight.constant", 1.0f); lightingShader.setFloat("spotLight.linear", 0.09); lightingShader.setFloat("spotLight.quadratic", 0.032); lightingShader.setFloat("spotLight.cutOff", glm::cos(glm::radians(12.5f))); lightingShader.setFloat("spotLight.outerCutOff", glm::cos(glm::radians(15.0f)));
// view/projection transformations
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
lightingShader.setMat4("projection", projection);
lightingShader.setMat4("view", view);
// world transformation
glm::mat4 model = glm::mat4(1.0f);
lightingShader.setMat4("model", model);
// bind diffuse map
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap);
// bind specular map
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap);
// render containers
glBindVertexArray(cubeVAO);
for (unsigned int i = 0; i < 10; i++)
{
// calculate the model matrix for each object and pass it to shader before drawing
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cubePositions[i]);
float angle = 20.0f * i;
model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
lightingShader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
// also draw the lamp object(s)
lightCubeShader.use();
lightCubeShader.setMat4("projection", projection);
lightCubeShader.setMat4("view", view);
// we now draw as many light bulbs as we have point lights.
glBindVertexArray(lightCubeVAO);
for (unsigned int i = 0; i < 4; i++)
{
model = glm::mat4(1.0f);
model = glm::translate(model, pointLightPositions[i]);
model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
lightCubeShader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
I finally open source my game engine ENTIERLY made in java
https://www.reddit.com/r/opengl/s/Z47RJPUHCq (this is the old post that was showcasing it) After ~5 months of work, here it is ! It's in it's BETA but it has enough features for making real games !
Unfortunatly not docs for now but it's planned !
Marching Tetrahedra - Volumetric Render Engine (OpenGL/C++) (Opensource)
We added Marching Tetrahedra Rendering effect to our Volumetric Render Engine.
Here, we Render our Volume data as a set of Polygon meshes by extracting 'iso surface'. It goes through whole dataset and tries to fit a polygon based on data values to calculate a polygonal mesh from the volume dataset.
Here's the Git Repo Link - https://github.com/mikejernil/volumetric-render-engine
We are building this over at 3D ENGINERD. & are planning to push our implement more features starting with Custom file loading, and support for more volumetric formats like DICOM, VDB etc.
AO46: RGB32 Buffer Views + GL 4.3 SSBO Atomic Groundwork
- RGB32 buffer views now support real
FLOAT,UINT, andSINTvariants through live sampler state, with the unsigned path retaining hardware draw/readback coverage. - GL 4.3 groundwork now includes static-index SSBO atomics lowering through Mesa and executing a fenced 32-thread atomic-add verification.
1000 vs 1000 full lod + shadows, custom engine
need help with drawing text
i've been trying to tackle this problem for a while now and luckily it works, well ... kinda. I see the text but the glyphs are always slightly misplaced, i have a feeling it's floating-point inaccuracies but i have no idea how to fix it, here are the snippets:
```c
void drawText(WS_Shell* shell, GlyphCacheDA* cache, char* text, FT_Face font, float x, float y) {
float pen_x = x;
for (char* c = text; *c != '\0'; c++) {
uint32_t codepoint = next_utf8(&text);
bool is_drawn = false;
if (codepoint == ' ') {pen_x += cache->items[0].advance; continue;} // if space, just advance
// search in cache first
for (int i = 0; i<cache->count; i++) {
if (cache->items[i].codepoint == codepoint) {
drawTexturedRectangle(pen_x, y, cache->items[i].width, cache->items[i].height, cache->items[i].texture);
pen_x += cache->items[i].advance;
is_drawn = true;
break;
}
}
if (is_drawn) continue;
FT_Load_Glyph(font, FT_Get_Char_Index(font, codepoint), FT_LOAD_RENDER);
FT_Render_Glyph(font->glyph, FT_RENDER_MODE_NORMAL);
GLuint glyph_texture;
glGenTextures(1, &glyph_texture);
glBindTexture(GL_TEXTURE_2D, glyph_texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, font->glyph->bitmap.width, font->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, font->glyph->bitmap.buffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Glyph glyph = {
.codepoint = codepoint,
.texture = glyph_texture,
.width = ((float)font->glyph->bitmap.width/shell->settings->width)*2,
.height = ((float)font->glyph->bitmap.rows/shell->settings->height)*2,
};
// TODO: add support for non-monospace fonts
glyph.advance = glyph.width;
nob_da_append(cache, glyph);
drawTexturedRectangle(pen_x, y, glyph.width, glyph.height, glyph.texture);
pen_x += glyph.advance;
is_drawn = false;
}
}
```
draw rectangle:
```c
void drawTexturedRectangle(float x, float y, float w, float h, GLuint texture) {
// Vertex data for the rectangle
float vertices[] = {
x, y, 0.0f, 1.0f,
x + w, y, 1.0f, 1.0f,
x, y + h, 0.0f, 0.0f,
x + w, y + h, 1.0f, 0.0f
};
// Indices for the rectangle
unsigned int indices[] = {
0, 1, 2,
1, 3, 2
};
// VBO and VAO
GLuint VBO, VAO, EBO;
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Draw the rectangle
glBindTexture(GL_TEXTURE_2D, texture);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// Clean up
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glDeleteBuffers(1, &VAO);
}
```
the dinamic array for cache i stolen from nob.h, each glyph's cache looks like this:
```c
typedef struct {
uint32_t codepoint;
GLuint texture;
float width;
float height;
float advance;
} Glyph;
```
everything else is pretty standard opengl stuff, thanks for helping
Built a game engine
I've been working on my own game engine from scratch for some years using C and OpenGL and I finally made a video showing the process.
The engine handles things like rendering, assets, shaders, lighting, and more, and I also used it to make a game, a test game though, it still needs a lot of polish.
Watch video here: https://youtu.be/LRC4iJWASYU?si=_3ua8rcwesyaQtO9
If you're interested in graphics programming, game engines, I'd love to hear your thoughts and feedback.
Hello, I am new to opengl. Can someone explain compute shaders to me
so i recently started playing around with opengl and wanted to try and make a raytracer with compute shaders, but there isn't much info about them. i looked at the tutorial on learnopengl but that wasn't very helpful. can someone clue me in?
Well.... We did meet a hard boundary finally , beyond which continuing would be risky for systems
After a few weeks of pushing AO46 much further than I originally expected, I think I’ve finally reached the point where continuing the same reverse-engineering path would cross from graphics-driver research into territory I’m not comfortable touching on real machines.
For context, AO46 started as an attempt to replace Apple’s deprecated OpenGL stack with a modern OpenGL 4.6 implementation on macOS.
The project has already moved through several architecture stages:
- replacing the old OpenGL.framework-facing stack,
- Mesa/Gallium integration,
- NIR,
- Asahi’s AGX compiler/backend work,
- macOS-specific resource management,
- GPU queue/submission tracing,
- and finally following the path between Apple-generated GPU code and the actual executable GPU mapping used by the kernel driver.
For quite a while I assumed the remaining problem was simply:
“figure out how macOS submits the same AGX command buffers that Asahi submits on Linux.”
It turns out that was too simple.
What the reverse pass has increasingly shown is that Apple does not treat executable GPU code as just another buffer with a magic flag.
There is a fairly clear trust boundary.
Very roughly, the path looks like:
Apple GPU compiler output
↓
Apple-owned code resource
↓
private relocation / preparation step
↓
restricted executable GPU mapping
↓
queue consumption
Generic buffers do not appear to just become executable after the fact.
And the important bit is that the transition into that executable mapping is not exposed like a normal public allocation API.
At this point, the remaining work would mean investigating the enforcement side of that boundary rather than merely understanding the graphics ABI around it.
That is where I’m stopping.
Not because the project suddenly became impossible, but because there is a difference between:
reverse engineering an undocumented graphics driver
and
deliberately trying to defeat a platform security boundary in order to make arbitrary GPU memory executable.
The latter is not something I want AO46 to become.
And honestly, that boundary existing is probably a good thing.
So is AO46 dead?
No.
Not even remotely.
A huge amount of useful architecture now exists that did not exist a few weeks ago.
The project now has concrete implementations/documentation for:
- OpenGL.framework replacement
- CGL/NSOpenGL compatibility
- Mesa Gallium integration
- NIR shader pipeline
- AGX compiler integration
- macOS BO/resource handling
- synchronization/fence ownership
- queue tracing
- Apple GPU submission structure analysis
- Apple compiler-object parsing
- executable-code provenance tracking
The original project was basically:
OpenGL
↓
Mesa
↓
Metal
The current research has gone much deeper:
OpenGL
↓
Mesa
↓
Gallium
↓
NIR
↓
AGX backend
↓
macOS GPU infrastructure
That is still extremely valuable.
What we do not currently have is a legitimate way to complete the final transition:
Mesa-generated AGX code
↓
???
↓
Apple-authorized executable GPU mapping
And fabricating or bypassing that transition is exactly the point where I’m drawing the line.
Interestingly, this also answers one of the biggest questions people kept asking
A lot of people assumed the blocker would be:
- AGX ISA differences,
- WindowServer,
- Metal interoperability,
- command buffer encoding,
- or simply “Apple doesn’t expose IOKit.”
Those are all problems.
But they were not the final one.
The deepest blocker is much more architectural:
Apple owns the transition that turns compiled GPU code into something the GPU is actually allowed to execute.
That is a considerably stronger boundary than I expected when this project started.
What happens next?
Probably one of three directions.
1. Stay above the protected execution boundary
Use Apple-supported APIs for the final executable-code handoff while keeping as much of Mesa/AGX/OpenGL outside that boundary as possible.
This is currently the most realistic direction.
2. Continue documenting the architecture
There is still a huge amount of useful work that can be done without trying to bypass anything.
For example:
- command submission structures
- resource lifetime rules
- synchronization semantics
- shader metadata
- queue behavior
- compiler object formats
- AGX generation differences
All of that is legitimate reverse-engineering work and could be useful far beyond AO46.
3. Wait for a better supported interface
Apple may eventually expose more GPU infrastructure through DriverKit, Metal evolution, or some future API.
If a legitimate executable-code path appears, AO46 can plug into it.
The upper 90% of the architecture would not need to be thrown away.
Honestly, I’m pretty happy we found this
This might sound weird, but discovering a hard architectural boundary is actually a useful result.
A month ago the unanswered question was:
“Can Mesa/Asahi actually talk to Apple’s GPU stack on macOS?”
Now the question is much narrower:
“How can externally generated AGX code legitimately enter Apple’s trusted executable-code pipeline?”
That is a much better-defined problem.
And importantly, we now know where not to push.
So for now the project is stepping back from the protected execution path and focusing on everything around it.
AO46 is still alive.
The research path just finally reached a sign that says:
┌─────────────────────────────┐
│ HERE BE SECURITY POLICY │
│ │
│ graphics engineers pls stop │
└─────────────────────────────┘
Which, considering how absurdly deep this project has gone in three weeks, was probably inevitable.
VertexArt - this is how it started (Intel N4100 CPU / Free Pascal 3.2.2 / GLFW3 / OpenGL 3.3 / data-oriented / only 16 byte vertex, nothing else)
Can't get the project to load textures
Hello,
This is my project, perfectly up to date: opengl
When i launch it, it throws this error log: unit 0 GLD_TEXTURE_INDEX_2D is unloadable and bound to sampler type (Float) - using zero texture because texture unloadable, and doesn't load my model
To note it worked before adding the move constructor to the shader.h and mesh.h, but reverting them doesn't work
I've tried everything, scouted forums, asked AI (reluctantly), but nothing worked, so I'm asking here for help from people smarter then me
Thanks for the time.
3D Volumetric Render Engine (OpenGL & C++) (Colormap)
Hey Everyone - we at 3D ENGINERD. are building a Volumetric Render Engine for Windows(Native). It's being built with OpenGL & C++
We're planning to publish the code open-source under MIT License on our Github (https://github.com/mikejernil) tomorrow, so you all can try it out and use it for your own applications. ✨
Currently it has -
- Volumetric RAW visualization support
- Different types of rendering (Colormap, Iso-surface etc.)
- Rotate & Zoom Controls (for easy navigation)
- 6 slicing planes to visualization cross-sections
We are planning to build more features and add support for more volumetric formats (like DICOM, VDB etc) soon!
Colormap Classification of an Internal Combustion Engine(shown in video):
Here we can see the volume data with colour values mapped to its material density.
As per our current Colormap we can see the Red is Higher density whereas blue is Low density Noise.
Applications :
- Medical imaging
- Industrial testing
- Scientific visualization of data
It's till very early-stages and we're actively exploring into Volumetric rendering at the moment, any constructive feedback would be appreciated, thanks! :)
Sunlit Kingdom, Free Demo on Steam!
Just released the demo for Sunlit Kingdom, a 3D tower defense where you place and control three hero types instead of static towers.
Each hero is highly customisable & unique, different abilities, etc. Game's built in a custom engine.
Demo's free on Steam: https://store.steampowered.com/app/5011470/Sunlit_Kingdom/
I hope u guys enjoy it! Any feedback is welcome.
SSAO optimization
Hi folks,
I realized that the SSAO implementation from LearnOpenGL had become a real bottleneck in my renderer.
I was getting around 45 FPS, so I profiled the pass and made a few changes:
● Reduced the SSAO framebuffer resolution
● Got rid of position/depth reconstruction
● Reduced the kernel size
After the changes, I’m getting around 77 FPS with very little noticeable visual difference.
45 FPS → 77 FPS just by making the SSAO pass do less work.
It’s a good reminder that tutorial implementations are great for learning, but once you’re building an actual renderer, you eventually have to question every piece of work you’re asking the GPU to do.
Github: https://github.com/xms0g/abra