GraysonPeddie's picture

Non-Circular Light in Edge of Face; Light Disappears in Center

Hi, everyone. I have a couple of codes and an image to show you what's going on, so what I'm trying to do is project a rounded light, but now, the light is quite weird. Have a look at my vertex/fragment code:

Vertex Shader (main.vert):

#version 140
 
// World space
uniform mat4 world_matrix;
 
// object space to camera space transformation
uniform mat4 view_matrix;            
 
// camera space to clip coordinates
uniform mat4 projection_matrix;
 
// incoming vertex position
in vec3 vertexPosition;
 
// incoming vertex normal
in vec3 vertexNormal;
 
out vec3 position, normal;
 
void main(void)
{
    normal = normalize((world_matrix * vec4(vertexNormal, 1)).xyz);
 
    vec4 worldPosition = world_matrix * vec4(vertexPosition, 1);
    vec4 viewPosition = view_matrix * worldPosition;
    gl_Position = projection_matrix * viewPosition;
	position = worldPosition.xyz;
}

Fragment Shader (Main.frag):

#version 140
 
precision highp float;
 
uniform vec3 lightPosition;
uniform vec4 lightColor, modelColor;
 
in vec3 position, normal;
 
out vec4 out_frag_color;
 
void main(void)
{
    vec3 lightVector = lightPosition - position;
 
    float dist = length(lightVector);
    // attenuation = 1.0 / (constantFactor + linearFactor * distance + quadraticFactor * distance * distance)
    float attenuationDiffuse = 1.0 / (0.0 + 1.0 * dist + 0.0 * dist * dist);
    lightVector = normalize(lightVector);
 
    float nxDir = max(0.0, dot(normal, lightVector));
    out_frag_color = modelColor * lightColor * nxDir * attenuationDiffuse +
                 vec4(0.1, 0.1, 0.1, 1.0);
}

Here is the image:

I will give you the whole code so you can try them out:

MainGame.cs:

using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
 
namespace HelloOpenGL
{
    class MainGame : GameWindow
    {
        string vertexShaderSource, fragmentShaderSource;
 
        int vertexShaderHandle,
            fragmentShaderHandle,
            shaderProgramHandle,
            worldMatrixLocation,
            viewMatrixLocation,
            projectionMatrixLocation,
            modelColorLocation,
            lightColorLocation,
            lightPositionLocation;
 
        int cubeVboHandle,
            cubeIndicesVboHandle,
            floorVboHandle,
            floorIndicesVboHandle;
 
        Vector3[] cubePositionData = new Vector3[]{
            new Vector3(-0.5f, -0.5f,  0.5f),
            new Vector3( 0.5f, -0.5f,  0.5f),
            new Vector3( 0.5f,  0.5f,  0.5f),
            new Vector3(-0.5f,  0.5f,  0.5f),
            new Vector3(-0.5f, -0.5f, -0.5f),
            new Vector3( 0.5f, -0.5f, -0.5f),
            new Vector3( 0.5f,  0.5f, -0.5f),
            new Vector3(-0.5f,  0.5f, -0.5f)};
 
        Vector3[] cubeNormalData = new Vector3[]{
            new Vector3( 0.0f,  0.0f,  1.0f),
            new Vector3( 0.0f,  1.0f,  0.0f),
            new Vector3( 1.0f,  0.0f,  0.0f),
            new Vector3( 0.0f,  0.0f, -1.0f),
            new Vector3( 0.0f, -1.0f,  0.0f),
            new Vector3(-1.0f,  0.0f,  0.0f)};
 
        VertexPositionNormal[] cube;
        VertexPositionNormal[] floor = new VertexPositionNormal[] {
            new VertexPositionNormal(-32, 0, -32, 0, 1, 0),
            new VertexPositionNormal(32, 0, -32, 0, 1, 0),
            new VertexPositionNormal(32, 0, 32, 0, 1, 0),
            new VertexPositionNormal(-32, 0, 32, 0, 1, 0)};
 
        // Projection and View matrix for camera; world matrix for object
        Matrix4 matProjection, matView, matWorld;
 
        ushort[] cubeIndicesVboData = new ushort[]{
             0,  1,  2,  2,  3,  0,   // Front
             4,  5,  6,  6,  7,  4,   // Top
             8,  9, 10, 10, 11,  8,   // Back
            12, 13, 14, 14, 15, 12,   // Right
            16, 17, 18, 18, 19, 16,   // Bottom
            20, 21, 22, 22, 23, 20};  // Left
 
        ushort[] floorIndicesVboData = new ushort[]{
            0, 1, 2, 2, 3, 0};
 
        public MainGame()
            : base(1280, 720, new GraphicsMode(new ColorFormat(32)),
            "Hello OpenGL!", 0, DisplayDevice.Default,
            3, 1, GraphicsContextFlags.Debug)
        {
        }
 
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
 
            cube = new VertexPositionNormal[]{
                new VertexPositionNormal(cubePositionData[0], cubeNormalData[0]),
                new VertexPositionNormal(cubePositionData[1], cubeNormalData[0]),
                new VertexPositionNormal(cubePositionData[2], cubeNormalData[0]),
                new VertexPositionNormal(cubePositionData[3], cubeNormalData[0]),
                new VertexPositionNormal(cubePositionData[3], cubeNormalData[1]),
                new VertexPositionNormal(cubePositionData[2], cubeNormalData[1]),
                new VertexPositionNormal(cubePositionData[6], cubeNormalData[1]),
                new VertexPositionNormal(cubePositionData[7], cubeNormalData[1]),
                new VertexPositionNormal(cubePositionData[1], cubeNormalData[2]),
                new VertexPositionNormal(cubePositionData[5], cubeNormalData[2]),
                new VertexPositionNormal(cubePositionData[6], cubeNormalData[2]),
                new VertexPositionNormal(cubePositionData[2], cubeNormalData[2]),
                new VertexPositionNormal(cubePositionData[7], cubeNormalData[3]),
                new VertexPositionNormal(cubePositionData[6], cubeNormalData[3]),
                new VertexPositionNormal(cubePositionData[5], cubeNormalData[3]),
                new VertexPositionNormal(cubePositionData[4], cubeNormalData[3]),
                new VertexPositionNormal(cubePositionData[0], cubeNormalData[4]),
                new VertexPositionNormal(cubePositionData[1], cubeNormalData[4]),
                new VertexPositionNormal(cubePositionData[5], cubeNormalData[4]),
                new VertexPositionNormal(cubePositionData[4], cubeNormalData[4]),
                new VertexPositionNormal(cubePositionData[4], cubeNormalData[5]),
                new VertexPositionNormal(cubePositionData[0], cubeNormalData[5]),
                new VertexPositionNormal(cubePositionData[3], cubeNormalData[5]),
                new VertexPositionNormal(cubePositionData[7], cubeNormalData[5])};
 
            vertexShaderSource = ReadFileToString("Shaders\\main.vert");
            fragmentShaderSource = ReadFileToString("Shaders\\main.frag");
 
            CreateShaders();
            CreateProgram();
            GL.UseProgram(shaderProgramHandle);
 
            QueryLocations();
 
            float aspectRatio = (float)(ClientSize.Width) / (float)(ClientSize.Height);
 
            SetProjectionMatrix(Matrix4.CreatePerspectiveFieldOfView(
                ((float)(Math.PI) / 180.0f) * 45.0f, aspectRatio, 1.0f, 20.0f));
            SetViewMatrix(Matrix4.CreateRotationX(0.5f) * Matrix4.CreateTranslation(0, 0, -16));
 
            GL.GenBuffers(1, out floorVboHandle);
            GL.BindBuffer(BufferTarget.ArrayBuffer, floorVboHandle);
            GL.BufferData<VertexPositionNormal>(BufferTarget.ArrayBuffer,
                new IntPtr(floor.Length * VertexPositionNormal.SizeInBytes),
                floor, BufferUsageHint.StaticDraw);
 
            floorIndicesVboHandle = LoadIndexer(floorIndicesVboData);
 
            GL.GenBuffers(1, out cubeVboHandle);
            GL.BindBuffer(BufferTarget.ArrayBuffer, cubeVboHandle);
            GL.BufferData<VertexPositionNormal>(BufferTarget.ArrayBuffer,
                new IntPtr(cube.Length * VertexPositionNormal.SizeInBytes),
                cube, BufferUsageHint.StaticDraw);
 
            cubeIndicesVboHandle = LoadIndexer(cubeIndicesVboData);
 
            LoadVertexType(0, "vertexPosition");
            LoadVertexType(1, "vertexNormal");
 
            GL.Uniform4(lightColorLocation, Color4.White);
 
            GL.Enable(EnableCap.DepthTest);
            GL.ClearColor(Color4.Black);
            Keyboard.KeyDown += new EventHandler<OpenTK.Input.KeyboardKeyEventArgs>(Keyboard_KeyDown);
            this.WindowBorder = OpenTK.WindowBorder.Fixed;
        }
 
 
 
        private string ReadFileToString(string fileName)
        {
            bool error = false;
            string result = String.Empty;
            if (File.Exists(fileName))
            {
                StreamReader file = null;
                string line;
                try
                {
                    file = new StreamReader(fileName);
                    while ((line = file.ReadLine()) != null)
                    {
                        result += line + "\n";
                    }
                }
                catch
                {
                    MessageBox.Show("Error processing the file:\n\n" + fileName, Title);
                    error = true;
                }
                finally
                {
                    if (file != null) { file.Close(); file = null; }
                }
            }
            else
            {
                MessageBox.Show(fileName + " does not exist.", Title);
                error = true;
            }
 
            if (error) Exit();
            return result;
        }
 
        private void CreateShaders()
        {
            vertexShaderHandle = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vertexShaderHandle, vertexShaderSource);
            GL.CompileShader(vertexShaderHandle);
 
            Console.WriteLine(GL.GetShaderInfoLog(vertexShaderHandle));
 
            fragmentShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fragmentShaderHandle, fragmentShaderSource);
            GL.CompileShader(fragmentShaderHandle);
 
            Console.WriteLine(GL.GetShaderInfoLog(fragmentShaderHandle));
        }
 
        private void CreateProgram()
        {
            shaderProgramHandle = GL.CreateProgram();
 
            GL.AttachShader(shaderProgramHandle, vertexShaderHandle);
            GL.AttachShader(shaderProgramHandle, fragmentShaderHandle);
 
            GL.LinkProgram(shaderProgramHandle);
 
            Console.WriteLine(GL.GetProgramInfoLog(shaderProgramHandle));
        }
 
        private void QueryLocations()
        {
            projectionMatrixLocation = GL.GetUniformLocation(shaderProgramHandle, "projection_matrix");
            viewMatrixLocation = GL.GetUniformLocation(shaderProgramHandle, "view_matrix");
            worldMatrixLocation = GL.GetUniformLocation(shaderProgramHandle, "world_matrix");
            modelColorLocation = GL.GetUniformLocation(shaderProgramHandle, "modelColor");
            lightColorLocation = GL.GetUniformLocation(shaderProgramHandle, "lightColor");
            lightPositionLocation = GL.GetUniformLocation(shaderProgramHandle, "lightPosition");
        }
 
        private void SetWorldMatrix(Matrix4 matrix)
        {
            matWorld = matrix;
            GL.UniformMatrix4(worldMatrixLocation, false, ref matWorld);
        }
 
        private void SetViewMatrix(Matrix4 matrix)
        {
            matView = matrix;
            GL.UniformMatrix4(viewMatrixLocation, false, ref matView);
        }
 
        private void SetProjectionMatrix(Matrix4 matrix)
        {
            matProjection = matrix;
            GL.UniformMatrix4(projectionMatrixLocation, false, ref matProjection);
        }
 
        private void LoadVertexType(int index, string type)
        {
            GL.EnableVertexAttribArray(index);
            GL.BindAttribLocation(shaderProgramHandle, index, type);
            GL.VertexAttribPointer(index, 3, VertexAttribPointerType.Float, false, VertexPositionNormal.SizeInBytes, 0);
        }
 
        private int LoadIndexer(ushort[] indices)
        {
            int handle;
            GL.GenBuffers( 1, out handle );
            GL.BindBuffer( BufferTarget.ElementArrayBuffer, handle );
            GL.BufferData<ushort>( BufferTarget.ElementArrayBuffer,
                new IntPtr( indices.Length * sizeof(ushort) ),
                indices, BufferUsageHint.StaticDraw );
            return handle;
        }
 
        void Keyboard_KeyDown(object sender, OpenTK.Input.KeyboardKeyEventArgs e)
        {
            switch (e.Key)
            {
                case OpenTK.Input.Key.Escape: Exit(); break;
            }
        }
 
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
 
        }
 
        float rotateY = 0, moveLight = 0;
 
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            GL.Viewport(0, 0, this.Width, this.Height);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
            SetViewMatrix(matView);
 
            GL.Uniform3(lightPositionLocation, new Vector3((float)Math.Cos(moveLight) * 8.0f, 2, 0));
 
            GL.BindBuffer(BufferTarget.ArrayBuffer, floorVboHandle);
            GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
            GL.NormalPointer(NormalPointerType.Float, 0, 0);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, floorIndicesVboHandle);
 
            GL.Uniform4(modelColorLocation, Color4.White);
            SetWorldMatrix(
                Matrix4.CreateRotationX((float)Math.PI / 180 * 270) *
                Matrix4.CreateTranslation(0, -0.5f, 0) *
                Matrix4.Scale(16.0f));
            GL.DrawElements(BeginMode.Triangles, floorIndicesVboData.Length, DrawElementsType.UnsignedShort, 0);
 
            /*GL.BindBuffer(BufferTarget.ArrayBuffer, cubeVboHandle);
            GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
            GL.NormalPointer(NormalPointerType.Float, 0, 0);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, cubeIndicesVboHandle);
 
            GL.Uniform4(modelColorLocation, Color4.Blue);
            SetWorldMatrix(Matrix4.CreateRotationY((float)e.Time + rotateY) *
                Matrix4.CreateTranslation(0, 0.5f, 0));
            GL.DrawElements(BeginMode.Triangles, cubeIndicesVboData.Length, DrawElementsType.UnsignedShort, 0);*/
 
            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
 
            rotateY += (float)Math.PI / 360.0f;
            moveLight += (float)Math.PI / 1440.0f;
 
            GL.Flush();
            SwapBuffers();
        }
 
    }
}

VertexPositionNormal.cs:

using System;
using OpenTK;
 
namespace HelloOpenGL
{
    public struct VertexPositionNormal
    {
        public Vector3 position, normal;
 
        public VertexPositionNormal(float x, float y, float z, float nx, float ny, float nz)
        {
            position = new Vector3(x, y, z); normal = new Vector3(nx, ny, nz);
        }
 
        public VertexPositionNormal(Vector3 position, Vector3 normal)
        {
            this.position = position;
            this.normal = normal;
        }
 
        public static int SizeInBytes { get { return sizeof(float) * 6; } }
    }
}

I have commented out the spinning cube code so you can see the effect without the cube shown).


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
GraysonPeddie's picture

Hi, everyone. I found a way to get the light rounded in the surface of th polygons.

Here's the vertex shader:

#version 140
 
// World space
uniform mat4 world_matrix, view_matrix, projection_matrix;
uniform vec3 lightPosition;
in vec3 vertexPosition, vertexNormal;
 
out vec3 position, normal, lightVector;
out vec4 color;
 
void main(void)
{
    normal = normalize((world_matrix * vec4(vertexNormal, 1)).xyz);
 
    vec4 worldPosition = world_matrix * vec4(vertexPosition, 1);
    vec4 viewPosition = view_matrix * worldPosition;
    gl_Position = projection_matrix * viewPosition;
	position = worldPosition.xyz;
 
	lightVector = normalize(lightVector);
 
    float nxDir = max(dot(normal, lightVector), 0.0);
 
	color = nxDir + vec4(0.1, 0.1, 0.1, 1.0);
}

And here's the fragment shader:

#version 140
 
precision highp float;
uniform vec3 lightPosition;
uniform vec4 lightColor, modelColor;
 
in vec3 position, normal, lightVector;
 
in vec4 color;
out vec4 out_frag_color;
 
void main(void)
{
    vec3 lightVector = lightPosition - position;
	float dist = length(lightVector);
 
    float att = 1.0 / (0.0 + 0.125 * dist + 0.0 * dist * dist);
 
    out_frag_color = color * modelColor * lightColor * att;
}

The result?

Well, what can I say? It took me a very long time to figure this out. It's trial and error until I get it right. Now, if only I could figure out how to adjust the radius of the point light... And I think it'll be interesting to try out uniform arrays for multiple dynamic lights! :) (Hmm...welcome to the world of programmable shaders. :))

Update: Here's an improved version of vertex and fragment shaders:

Vertex Shader:

#version 140
#extension GL_EXT_gpu_shader4 : enable
 
const int MAXLIGHTS = 4;
 
// World space
uniform mat4 world_matrix, view_matrix, projection_matrix;
 
in vec3 vertexPosition, vertexNormal;
 
out vec3 position, normal;
out vec4 color[MAXLIGHTS];
 
void main(void)
{
    normal = normalize((world_matrix * vec4(vertexNormal, 1)).xyz);
 
    vec4 worldPosition = world_matrix * vec4(vertexPosition, 1);
    vec4 viewPosition = view_matrix * worldPosition;
    gl_Position = projection_matrix * viewPosition;
	position = worldPosition.xyz;
 
	for(int i = 0; i < MAXLIGHTS; i++)
	{
	    color[i] = vec4(0.05, 0.05, 0.05, 1.0);
    }
}

Fragment Shader:

#version 140
 
const int MAXLIGHTS = 4;
 
precision highp float;
 
uniform vec3 lightPosition[MAXLIGHTS];
uniform vec4 lightColor[MAXLIGHTS], modelColor;
 
in vec3 position, normal;
 
in vec4 color[MAXLIGHTS];
out vec4 out_frag_color;
 
void main(void)
{
    for(int i = 0; i < MAXLIGHTS; i++)
	{
        float dist = length(lightPosition[i] - position);
 
        // attenuation = 1.0 / (constantFactor + linearFactor * distance + quadraticFactor * distance * distance)
        float att = 1.0 / (0.0 + 0.125 * dist + 0.0 * dist * dist);
 
        out_frag_color += color[i] * modelColor * lightColor[i] * att;
	}
}

This is a clean-up version compared to my previous shader code from above.

I use multiple dynamic lights. For beginners, if you're going to need 4 lights in an array, your must create multiple location handles by doing something like this:

for (int i = 0; i < 4; i++)
            {
                lightColorLocation[i] = GL.GetUniformLocation(shaderProgramHandle, "lightColor[" + i + "]");
                lightPositionLocation[i] = GL.GetUniformLocation(shaderProgramHandle, "lightPosition[" + i + "]");
            }

Then, when you are going to set values to the variables in GLSL, you can do this:

            GL.Uniform4(lightColorLocation[0], Color4.White);
            GL.Uniform4(lightColorLocation[1], Color4.Yellow);
            GL.Uniform4(lightColorLocation[2], Color4.Green);
            GL.Uniform4(lightColorLocation[3], Color4.Red);
 
// ..
 
            GL.Uniform3(lightPositionLocation[0], new Vector3(
                (float)Math.Cos(moveLight) * 2,
                1.5f,
                (float)Math.Sin(moveLight) * 2));
 
            GL.Uniform3(lightPositionLocation[1], new Vector3(
                (float)Math.Cos(moveLight / 2.0f) * 4.5f,
                2,
                (float)Math.Sin(moveLight / 1.5f) * 2.5f));
 
            GL.Uniform3(lightPositionLocation[2], new Vector3(
                (float)Math.Cos(moveLight * 2.5f) * 8f,
                1f,
                (float)Math.Sin(moveLight * 4) * 2.5f));
 
            GL.Uniform3(lightPositionLocation[3], new Vector3(
                (float)Math.Cos(moveLight / 1.5f) * 6,
                3f,
                (float)Math.Sin(moveLight / 2.0f) * 8));

Here is my final result:

Neat, huh? Well, I hope I can be of help to those who need it.

Now, if only I could figure it out how to adjust the duration of the light source; I mean, from the center of the light source to the point where there is no light left...

c2woody's picture
Quote:

Now, if only I could figure it out how to adjust the duration of the light source; I mean, from the center of the light source to the point where there is no light left...

This is usually called falloff, you should find some examples on the net (linear falloff, exponential etc.).

GraysonPeddie's picture

Hmm... Are you referring to this equation that I have in my code above?

attenuation = 1.0 / (constantFactor + linearFactor * distance + quadraticFactor * distance * distance)

I read Constant-Linear-Quadratic Falloff and I've realized that attenuation is falloff, but then I am starting to wonder how to increase the falloff (distance) without increasing the intensity of the light?

(A few minutes later...)

Hmm... This seems to make sense:

float falloff = 10;
float att = 1.0 / (0.3 / falloff + 0.6 / falloff * dist + 0.1 / falloff * dist * dist);

I'm thinking it might be impossible to not increase the intensity of the light while still increase the distance of the falloff. It has taken me a lot of searching to find what I'm looking for, though.

the Fiddler's picture

Don't forget to clamp your attenuation:

float att = falloff / (0.3 + 0.6 * dist + 0.1 * dist * dist); // equivalent to your version
att = clamp(att, 0.0, 1.0);

Depending on the values of falloff and dist, your light can get brighter than 1.0 which is not what you want. Try this and see if it solves the light intensity issue.

GraysonPeddie's picture

Oh, know what? I think this is what I want. As I move the light source as far away from the surface of the plane (it's just 2 polygons connected together), the intensity decreases while the falloff distance is the same (a misunderstanding on my part). Well, here's my final code that I have:

    for(int i = 0; i < MAXLIGHTS; i++)
    {
        float dist = length(lightPosition[i] - position);
        float falloff = 12;
        // attenuation = falloff / (constantFactor + linearFactor * distance + quadraticFactor * distance * distance)
        float att = falloff / (0.0 + 0.2 * dist + 1 * dist * dist);
 
        out_frag_color += color[i] + modelColor * lightColor[i] * att;
    }

(The "color[i]" contains an array of ambient colors, so I changed the * to a + sign before modelColor, lightColor[i], and attenmuation gets multiplied.

Thanks for your help.

Who's online

There are currently 0 users and 15 guests online.