I am new to shader programming. I am trying to draw a circle with glsl. I used a point with a Size and tried to filter out the points outside the radius.(Altering the alpha value).
The code is as follows:
Fragment Shader:
#version 130
varying vec2 textureCoordinate;
const float circleBorderWidth = 0.08;//for anti aliasing
void main() {
float d = smoothstep(circleBorderWidth,0.1, 1.0-length(textureCoordinate));
gl_FragColor = vec4(0.0, 1.0, 0.0, d);
}
Vertex Shader:
#version 130
attribute vec4 coord3d;
attribute vec2 varPos;
varying vec2 textureCoordinate;
void
main()
{
textureCoordinate = varPos;
gl_FrontColor = gl_Color;
gl_Position = vec4(coord3d.xyz,1.);
gl_PointSize = coord3d.w;
}
Data:
float pos[] = {
-1, -1,
-1, 1,
1, 1,
1, -1,
};
float vertices[]={0.0,0.0f,0.0f,100.0f};
Draw Method:
void drawScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program->makeCurrent();
glEnable(GL_POINT_SMOOTH);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
glEnable(GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (varPosAttrib>=0) {
glVertexAttribPointer( varPosAttrib, 2, GL_FLOAT, GL_FALSE,
0, pos ); // -->varPos in Vertex Shader.
glEnableVertexAttribArray( varPosAttrib );
}
if (posAttrib>=0) {
glVertexAttribPointer(posAttrib, 4, GL_FLOAT, GL_FALSE, 0, vertices); // -->coord3d in vertex shader
glEnableVertexAttribArray(posAttrib);
glDrawArrays(GL_POINTS, 0, 1);
}
glDisable(GL_POINT_SMOOTH);
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
glDisable(GL_BLEND);
program->release();
glutSwapBuffers(); //Send the 3D scene to the screen
}
This results in drawing a square if I replace d with 1.0 in the following line (in the fragment shader):
gl_FragColor = vec4(0.0, 1.0, 0.0, d); // -> if d is replaced by 1.0
I tried to replace the x and y values in gl_FragColor with textureCoordinate.x and textureCoordinate.y. Result was black (so I assume the values are 0.0). The thing which I don't understand is that if I take the length of textureCoordinate than it is always 1.0.(experimented by replacing the value in gl_fragcolor). I am unable to figure out as to what I am doing wrong here. I was expecting the textureCoordinate value to interpolate with respect to the passed in data (varPos).
Here's my current attempt at it. It works, in the sense that it draw a disc with a smooth border. I use a distance field approach ie. I compute the distance from the disc's border
Fragment shader
#version 110
varying vec2 uv;
void
main() {
float border = 0.01;
float radius = 0.5;
vec4 color0 = vec4(0.0, 0.0, 0.0, 1.0);
vec4 color1 = vec4(1.0, 1.0, 1.0, 1.0);
vec2 m = uv - vec2(0.5, 0.5);
float dist = radius - sqrt(m.x * m.x + m.y * m.y);
float t = 0.0;
if (dist > border)
t = 1.0;
else if (dist > 0.0)
t = dist / border;
gl_FragColor = mix(color0, color1, t);
}
Vertex shader
#version 110
varying vec2 uv;
void
main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
uv = vec2(gl_MultiTexCoord0);
}
It's meant to be drawn on quad with texture coordinate (-0.5, -0.5)x(0.5, 0.5)
Related
In the following example I would like to manually create points (x, y, angle) from SFML then fill a circle around each point. The angle will be used later, for now I use it for debugging.
SFML draws 2 points
Vertex shader convert points to -1..1 range
Geometry shader creates squares at each point position and pass the center to fragment shader
Fragment shader would pain a circle within each square.
From my understanding, in the geometry shader I emit center which is the center coordinates of each primitive. From computing the distance from this center I would be able to paint a circle in each primitive from the fragment shader.
In the picture below I notice the center is only set once and I don't understand why.
SFML App
#include <iostream>
#include <SFML/Graphics.hpp>
#include <vector>
#include <GL/glew.h>
#include <random>
#define WIDTH 800
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, WIDTH), "Test");
sf::Shader shader;
shader.loadFromFile("shader.vert", "shader.geom", "shader.frag");
sf::Transform matrix = sf::Transform::Identity;
matrix.scale(1.0 / WIDTH, 1.0 / WIDTH);
sf::Glsl::Mat4 projectionViewMatrix = matrix;
shader.setUniform("projectionViewMatrix", projectionViewMatrix);
std::vector<GLfloat> vertices;
vertices.push_back(400.0); vertices.push_back(400.0); vertices.push_back(0.0);
vertices.push_back(400.0); vertices.push_back(-400.0); vertices.push_back(0.25);
vertices.push_back(-400.0); vertices.push_back(-400.0); vertices.push_back(0.5);
vertices.push_back(-400.0); vertices.push_back(400.0); vertices.push_back(0.75);
while (window.isOpen()) {
sf::Event currEvent;
while (window.pollEvent(currEvent)) {
switch (currEvent.type) {
case(sf::Event::Closed):
window.close(); break;
}
}
window.clear(sf::Color::Black);
glVertexPointer(3, GL_FLOAT, 0, vertices.data());
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_POINTS, 0, vertices.size() / 3);
glDisableClientState(GL_VERTEX_ARRAY);
sf::Shader::bind(&shader);
window.display();
}
}
Vertex Shader
#version 150
in vec3 position;
out vec3 pass_colour;
out float angle;
uniform mat4 projectionViewMatrix;
void main(void) {
gl_Position = projectionViewMatrix * vec4(position.xy, 0.0 ,1.0);
angle = position.z;
pass_colour = vec3(1.0);
}
Geometry shader
#version 150
layout (points) in;
layout (triangle_strip, max_vertices = 6) out;
in vec3 pass_colour[];
in float angle[];
out vec3 finalColour;
out vec4 centerPosition;
uniform mat4 projectionViewMatrix;
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void createVertex(vec3 offset, vec3 colour, float z = 0.0) {
vec4 actualOffset = vec4(offset, z);
vec4 worldPosition = gl_in[0].gl_Position + actualOffset;
gl_Position = worldPosition;
finalColour = colour;
vec4 pointPosition = gl_in[0].gl_Position;
centerPosition = pointPosition;
EmitVertex();
}
void main(void) {
float corner = 0.3;
vec3 colour = hsv2rgb(vec3(angle[0], 1.0, 1.0));
createVertex(vec3(-corner, -corner, 0.0), colour, 0.0);
createVertex(vec3(corner, -corner, 0.0), colour, 0.0);
createVertex(vec3(-corner, corner, 0.0), colour, 0.0);
createVertex(vec3(corner, corner, 0.0), colour, 0.0);
createVertex(vec3(corner, -corner, 0.0), colour, 0.0);
createVertex(vec3(-corner, corner, 0.0), colour, 0.0);
EndPrimitive();
}
Fragment shader
#version 150
in vec3 finalColour;
in vec4 centerPosition;
out vec4 out_Colour;
void main(void){
vec2 resolution = vec2(800.0/2.0, 800.0/2.0);
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec2 uvc = (centerPosition.xy + vec2(1.0)) / 2.0;
float dist = length(uv - uvc);
out_Colour = vec4(finalColour * dist, 0.8);
}
I still don't explain everything, but it works with this fragment:
#version 150
in vec4 finalColour;
in vec4 centerPosition;
out vec4 out_Colour;
void main(void){
vec2 resolution = vec2(800.0/2.0, 800.0/2.0);
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec2 p = vec2(1.0, 1.0) + centerPosition.xy;
vec2 uvc = p;
float dist = length(uv - uvc);
float col = 1.0 - smoothstep(0.0, 0.1, dist);
out_Colour = vec4(finalColour.rgb * col, 1.0);
}
I have a rotating cube which continuously rotates
But it always rendered in such a way with interpolated gradient color
I want to color single face with only one color exactly one triangle piece as below
My GLSL
#version 410 core
attribute vec3 position;
attribute vec3 color;
varying vec3 outColor;
uniform float time;
uniform mat4 matrix;
void main()
{
float theta = time;
float co = cos(theta);
float si = sin(theta);
mat4 rotationY = mat4(co, 0, si, 0,
0, 1, 0, 0,
-si, 0, co, 0,
0, 0, 0, 1);
mat4 rotationX = mat4(1, 0, 0, 0,
0, co, -si, 0,
0, si, co, 0,
0, 0, 0, 1);
outColor = color;
gl_Position = matrix * rotationY * rotationX * vec4(position,1.f);
}
#version 410 core
varying vec3 outColor;
uniform float time;
void main()
{
gl_FragColor = vec4(outColor,1);
}
My GLFW code
glClearColor(1f, 1f, 1f, 1f)
glClear(GL_COLOR_BUFFER_BIT.toUInt() or GL_DEPTH_BUFFER_BIT.toUInt())
var time = glfwGetTime()
glUniform1f!!(uniformTime, time.toFloat())
glDrawElements(GL_TRIANGLES, verticesIndex.size, GL_UNSIGNED_SHORT, NULL)
glfwSwapBuffers(window)
glfwPollEvents()
My color array
[
0.8, 0.007315, 0.026764,
0.01696, 0.8, 0.005661,
0.007843, 0.001524, 0.8,
0.01696, 0.8, 0.005661,
0.007843, 0.001524, 0.8,
0.01696, 0.8, 0.005661,
0.007843, 0.001524, 0.8,
0.01696, 0.8, 0.005661,
0.007843, 0.001524, 0.8,
0.01696, 0.8, 0.005661,
0.007843, 0.001524, 0.8,
0.01696, 0.8, 0.005661
]
The usual way is to create a mesh with color attributes where each face has its own vertices with a color attribute of the same color.
Alternatively, you can try using the flat Interpolation qualifier:
The value will not be interpolated. The value given to the fragment shader is the value from the Provoking Vertex for that primitive.
Vertex shader
#version 410 core
in vec3 position;
in vec3 color;
flat out vec3 outColor;
void main()
{
outColor = color
// [...]
}
Fragment shader:
#version 410 core
flat in vec3 outColor;
out vec4 fragColor;
void main()
{
fragColor = vec4(outColor, 1.0);
}
Another option would be to create an array of colors for each triangle primitive and store this color array in a Shader Storage Buffer Object. Use gl_VertexID to address the color in the vertex shader.
vec4 outColor;
layout(std430, binding = 0) buffer primitiveColors
{
vec4 colors[];
};
void main()
{
outColor = colors[gl_VertexID / 3];
// [...]
}
Following the learnopengl tutorial (https://learnopengl.com/Advanced-Lighting/Deferred-Shading)
the author leaves fixed the amount of light (32 lights) as shown by the GLSL:
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
struct Light {
vec3 Position;
color;
};
const int NR_LIGHTS = 32;
uniform Light lights [NR_LIGHTS];
uniform vec3 viewPos;
void main ()
{
// retrieve data from G-buffer
vec3 FragPos = texture (gPosition, TexCoords) .rgb;
vec3 Normal = texture (gNormal, TexCoords) .rgb;
vec3 Albedo = texture (gAlbedoSpec, TexCoords) .rgb;
float Specular = texture (gAlbedoSpec, TexCoords) .a;
// then calculate lighting as usual
vec3 lighting = Albedo * 0.1; // hard-coded ambient component
vec3 viewDir = normalize (viewPos - FragPos);
for (int i = 0; i <NR_LIGHTS; ++ i)
{
// diffuse
vec3 lightDir = normalize (lights [i] .Position - FragPos);
vec3 diffuse = max (dot (Normal, lightDir), 0.0) * Albedo * lights [i] .Color;
lighting + = diffuse;
}
FragColor = vec4 (lighting, 1.0);
}
And when it comes to applying the lights:
glBindFramebuffer (GL_FRAMEBUFFER, 0);
// 2. lighting pass: calculate lighting by iterating over screen filled quad pixel-by-pixel using the gbuffer's content.
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shaderLightingPass.use ();
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, gPosition);
glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, gNormal);
glActiveTexture (GL_TEXTURE2);
glBindTexture (GL_TEXTURE_2D, gAlbedoSpec);
// send light relevant uniforms
for (unsigned int i = 0; i <lightPositions.size (); i ++)
{
shaderLightingPass.setVec3 ("lights [" + std :: to_string (i) + "] .Position", lightPositions [i]);
shaderLightingPass.setVec3 ("lights [" + std :: to_string (i) + "] .Color", lightColors [i]);
// update attenuation parameters and calculate radius
const float constant = 1.0; // note that we do not send this to the shader, we assume it is always 1.0 (in our case)
const float linear = 0.7;
const float quadratic = 1.8;
shaderLightingPass.setFloat ("lights [" + std :: to_string (i) + "] .Linear", linear);
shaderLightingPass.setFloat ("lights [" + std :: to_string (i) + "] .Quadratic", quadratic);
}
shaderLightingPass.setVec3 ("viewPos", camera.Position);
// finally render quad
renderQuad ();
but I would like to be able to add as many lights as I want, because my project will have countless lights (laser guns, bonfire, blast), so I made some changes:
GLSL:
uniform Light light;
uniform vec3 viewPos;
void main()
{
// retrieve data from gbuffer
vec3 FragPos = texture(gPosition, TexCoords).rgb;
vec3 Normal = texture(gNormal, TexCoords).rgb;
vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb;
float Specular = texture(gAlbedoSpec, TexCoords).a;
// then calculate lighting as usual
vec3 lighting = Diffuse * 0.1; // hard-coded ambient component
vec3 viewDir = normalize(viewPos - FragPos);
// diffuse
vec3 lightDir = normalize(light.Position - FragPos);
vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * light.Color;
// specular
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(Normal, halfwayDir), 0.0), 16.0);
vec3 specular = light.Color * spec * Specular;
// attenuation
float distance = length(light.Position - FragPos);
float attenuation = 1.0 / (1.0 + light.Linear * distance + light.Quadratic * distance * distance);
diffuse *= attenuation;
specular *= attenuation;
lighting += diffuse + specular;
FragColor = vec4(lighting, 1.0);
}
And then I passed the values one by one and rendered a quad:
for (unsigned int i = 0; i < lightPositions.size(); i++)
{
shaderLightingPass.use();
shaderLightingPass.setInt("gPosition", 0);
shaderLightingPass.setInt("gNormal", 1);
shaderLightingPass.setInt("gAlbedoSpec", 2);
shaderLightingPass.setVec3("light.Position", lightPositions[i]);
shaderLightingPass.setVec3("light.Color", lightColors[i]);
const float constant = 1.0; // note that we don't send this to the shader, we assume it is always 1.0 (in our case)
const float linear = 0.7;
const float quadratic = 0.08;
shaderLightingPass.setFloat("light.Linear", linear);
shaderLightingPass.setFloat("light.Quadratic", quadratic);
shaderLightingPass.setVec3("viewPos", camera.Position);
renderQuad();
glUseProgram(-1);
}
and also added a new shader to render the framebuffer on the screen:
screenShader.use();
renderQuad();
but my code renders only the first light:
Result
could anyone tell me what I am doing wrong and how to add the lights in the end result?
Please include code like below
void renderDeferredPass(int i)
{
glUseProgram(ps[Passes::Deferred]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, g_fbo);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
//mat4 model = glm::scale(mat4(1.0f), vec3(3.1f, 3.1f, 3.1f));
model = glm::translate(mat4(1.0f), vec3(-150.0f, -600.0f, -800.0f+camera));
model = glm::rotate(model, 30.0f, vec3(0.0f, 1.0f, 0.0f));
mat4 view = glm::lookAt(glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 0.0, -5.0), glm::vec3(0.0, 1.0, 0.0));
glUniformMatrix4fv(modelLocation, 1, GL_FALSE, &model[0][0]);
glUniformMatrix4fv(viewLocation, 1, GL_FALSE, &view[0][0]);
glUniformMatrix4fv(projLocation, 1, GL_FALSE, &projection[0][0]);
glUniform1i(textureLocation, 0);
quad->Render();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glUseProgram(0);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
}
And
void renderLightPass()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);
glUseProgram(ps[Passes::LightPass]);
glBindVertexArray(quadVAO);
bindUbo();
for (unsigned int i = 0; i < NUM_GBUFFER_TEXTURES; i++)
{
glActiveTexture(GL_TEXTURE1 + i);
glBindTexture(GL_TEXTURE_2D,
g_textures[POSITION_TEXTURE + i]);
}
glUniform1i(mapLocations[POSITION_TEXTURE], 1);
glUniform1i(mapLocations[DIFFUSE_TEXTURE], 2);
glUniform1i(mapLocations[NORMAL_TEXTURE], 3);
glUniform1i(mapLocations[TEXCOORD_TEXTURE], 4);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
glUseProgram(0);
glBindVertexArray(0);
glEnable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
And your draw function should look like
void display()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glGenerateMipmap(GL_TEXTURE_2D);
glEnable(GL_MULTISAMPLE);
//for (int i = 0; i < quad->m_Entries.size(); i++)
{
renderDeferredPass(0);
renderLightPass();
}
glutSwapBuffers();
glutPostRedisplay();
}
For complete implementation refer following
https://github.com/PixelClear/Deferred-renderer
I have above code where we store light information in SSBO so this demo has 32 lights but can easily extended to many.
The problem is due to the fixed "ambient" term being repeated for the # of lights that overlap the scene.
The shader contains:
vec3 lighting = Diffuse * 0.1; // hard-coded ambient component
This effectively re-adds albedo everytime a light overlaps.
The old code had the following sum:
vec3 lighting = Diffuse * 0.1;
foreach (Light l : lights)
lighting += Diffuse * (l's diffuse lighting)
But now with additive blending you have:
foreach (Light l : lights)
lighting += Diffuse * 0.1;
lighting += Diffuse * (l's diffuse lighting)
As such you got the overbrightening of ambient in https://i.ibb.co/gMBtM6c/With-Blend.png
To fix this you need to separate the (Diffuse * 0.1) term into a separate shader. You would have 1 draw call to apply ambient, then n draw calls for n lights.
The algorithm on the C++ side would then look like:
Make sure you have additive blend still.
Clear Screen
Set Ambient shader, Draw Quad.
Set Light shader, Do your lighting loop and Draw n Quads for n lights.
EDIT: Also it looks like you aren't reading the right Albedo texture based off of your screenshots. It looks like you are reading the position buffer based off of the colors you're getting.
I have recently used a freetype library to read text files and followed some guide on how to display text in 2D.
I tried to extend the code to support 3D text rendering but i started having opengl related problems with it.
At certain angles the text picture starts to fade, and the whole axis on which the text is located starts to inherit its colour.
Fading;
Black Slice
All the related code is:
Drawing function (inherited from learnopengl.com)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Activate corresponding render state
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
glEnableVertexAttribArray(0);
scale /= RESOLUTION;
vec2 start(x, y);
// Iterate through all characters
std::string::const_iterator c;
for (c = text.begin(); c != text.end(); c++)
{
Character ch = Characters[*c];
if (*c == '\r' || (x-start.x > xMax && xMax != 0.0f))
{
y += ((ch.Advance >> 6) + 16) * scale ;
x = start.x;
continue;
}
GLfloat xpos = x + ch.Bearing.x * scale;
GLfloat ypos = y + (ch.Size.y - ch.Bearing.y) * scale;
GLfloat w = ch.Size.x * scale;
GLfloat h = ch.Size.y * scale;
// Update VBO for each character
GLfloat vertices[6][4] = {
{ xpos, ypos - h, 0.0, 0.0 },
{ xpos, ypos, 0.0, 1.0 },
{ xpos + w, ypos, 1.0, 1.0 },
{ xpos, ypos - h, 0.0, 0.0 },
{ xpos + w, ypos, 1.0, 1.0 },
{ xpos + w, ypos - h, 1.0, 0.0 }
};
// Render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.TextureID);
// Update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
// Render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (2^6 = 64)
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_BLEND);
Shader uniform initialization
ShaderBuilder::LoadShader(shader)->Add_mat4("projection", projection).Add_mat4("view", view).
Add_mat4("model", model).Add_vec3("textColor", color).Add_texture("text", 0);
Vertex Shader
#version 400 core
layout (location = 0) in vec4 vertex; //
out vec2 TexCoords;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
void main()
{
vec2 vertexGL = (vertex.xy - vec2(400,300)) / vec2(400,300);
vertexGL = vertex.xy;
vec4 position = projection * view * model * vec4(vertexGL.xy, 0.0, 1.0);
gl_Position = position / position.w;
TexCoords = vertex.zw;
}
Fragment Shader
#version 400 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
//color = vec4(1);
}
I finally found the mistake, for some unknown reason i thought normalizing my vertex coords after applying the matrix multiplication would be a good practice.
Apparently it isn't.
vec4 position = projection * view * model * vec4(vertexGL.xy, 0.0, 1.0);
gl_Position = position;// / position.w;
so as the commenting declares, this removed the mistake.
I'm trying to do point source directional lighting in OpenGL using my textbooks examples. I'm showing a rectangle centered at the origin, and doing the lighting computations in the shader. The rectangle appears, but it is black even when I try to put colored lights on it. Normals for the rectangle are all (0, 1.0, 0). I'm not doing any non-uniform scaling, so the regular model view matrix should also transform the normals.
I have code that sets the light parameters(as uniforms) and material parameters(also as uniforms) for the shader. There is no per vertex color information.
void InitMaterial()
{
color material_ambient = color(1.0, 0.0, 1.0);
color material_diffuse = color(1.0, 0.8, 0.0);
color material_specular = color(1.0, 0.8, 0.0);
float material_shininess = 100.0;
// set uniforms for current program
glUniform3fv(glGetUniformLocation(Programs[lightingType], "materialAmbient"), 1, material_ambient);
glUniform3fv(glGetUniformLocation(Programs[lightingType], "materialDiffuse"), 1, material_diffuse);
glUniform3fv(glGetUniformLocation(Programs[lightingType], "materialSpecular"), 1, material_specular);
glUniform1f(glGetUniformLocation(Programs[lightingType], "shininess"), material_shininess);
}
For the lights:
void InitLight()
{
// need light direction and light position
point4 light_position = point4(0.0, 0.0, -1.0, 0.0);
color light_ambient = color(0.2, 0.2, 0.2);
color light_diffuse = color(1.0, 1.0, 1.0);
color light_specular = color(1.0, 1.0, 1.0);
glUniform3fv(glGetUniformLocation(Programs[lightingType], "lightPosition"), 1, light_position);
glUniform3fv(glGetUniformLocation(Programs[lightingType], "lightAmbient"), 1, light_ambient);
glUniform3fv(glGetUniformLocation(Programs[lightingType], "lightDiffuse"), 1, light_diffuse);
glUniform3fv(glGetUniformLocation(Programs[lightingType], "lightSpecular"), 1, light_specular);
}
The fragment shader is a simple pass through shader that sets the color to the one input from the vertex shader. Here is the vertex shader :
#version 150
in vec4 vPosition;
in vec3 vNormal;
out vec4 color;
uniform vec4 materialAmbient, materialDiffuse, materialSpecular;
uniform vec4 lightAmbient, lightDiffuse, lightSpecular;
uniform float shininess;
uniform mat4 modelView;
uniform vec4 lightPosition;
uniform mat4 projection;
void main()
{
// Transform vertex position into eye coordinates
vec3 pos = (modelView * vPosition).xyz;
vec3 L = normalize(lightPosition.xyz - pos);
vec3 E = normalize(-pos);
vec3 H = normalize(L + E);
// Transform vertex normal into eye coordinates
vec3 N = normalize(modelView * vec4(vNormal, 0.0)).xyz;
// Compute terms in the illumination equation
vec4 ambient = materialAmbient * lightAmbient;
float Kd = max(dot(L, N), 0.0);
vec4 diffuse = Kd * materialDiffuse * lightDiffuse;
float Ks = pow(max(dot(N, H), 0.0), shininess);
vec4 specular = Ks * materialSpecular * lightSpecular;
if(dot(L, N) < 0.0) specular = vec4(0.0, 0.0, 0.0, 1.0);
gl_Position = projection * modelView * vPosition;
color = ambient + diffuse + specular;
color.a = 1.0;
}
Ok, it's working now. The solution was to replace glUniform3fv with glUniform4fv, I guess because the glsl counterpart is a vec4 instead of a vec3. I thought that it would be able to recognize this and simply add a 1.0 to the end, but no.