golang, OpenGL, computer graphics (3)

code repository

https://github.com/phprao/go-graphic

color

When the light source shines on the object, part of the color is absorbed, and the other part that cannot be absorbed is reflected to the human eye, so the color appears.

When we multiply the color of the light source with the color value of the object 相乘(rather than dot product), what we get is the color reflected by the object. From this, we can define the color of an object as the size of each color component reflected by the object from a light source.

You must know that the dot product of two vectors is a scalar. When two vectors are multiplied in GLSL, the result A * Bis a vector, which is the result of the multiplication of each component. The dot product in GLSL is dot(A, B): is a scalar quantity.

Basic lighting
Phong Lighting Model

It consists of 3 components: Ambient, Diffuse and Specular lighting.

Insert image description here

  • Ambient Lighting: Even in darkness, there is usually still some light in the world (moon, distant light), so objects are almost never completely dark. To simulate this, we'll use an ambient lighting constant that will always give the object some color.
  • Diffuse Lighting: simulates the directional impact of light sources on objects. It is the most visually significant component of the Feng lighting model. The closer a part of an object is to the light source, the brighter it will be.
  • Specular Lighting: Simulates the bright spots appearing on shiny objects. The color of specular lighting will be more inclined to the color of the light than the color of the object.
environmental care

Light usually does not come from one source, but from many sources scattered around us, even though they may not be obvious. One property of light is that it can diverge and bounce in many directions, allowing it to reach points that are not directly adjacent. Therefore, light can reflect off other surfaces and have an indirect effect on an object. An algorithm that takes this into account is called a Global Illumination algorithm, but this algorithm is expensive and extremely complex.

Since we are not very interested in complex and expensive algorithms right now, we will start with a simplified global illumination model, namely ambient illumination. As you learned in the previous section, we use a small constant (lighting) color that is added to the final color of the object fragment so that it looks diffuse even when there is no direct light source in the scene. of light.

Adding ambient lighting to your scene is very simple. We multiply the light's color by a small constant environment factor, multiply that by the object's color, and then use the final result as the fragment's color:

#version 410
    
out vec4 frag_colour;

uniform vec3 lightColor;
uniform vec3 objectColor;

void main() {
    
    
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;
    frag_colour = vec4(ambient * objectColor, 1.0);
}
diffuse lighting

Ambient lighting by itself doesn't provide the most interesting results, but diffuse lighting can start to have a significant visual impact on objects. Diffuse lighting allows segments of an object that are closer to the direction of the light to get more brightness from the light source.

Insert image description here

We know that the smaller the angle between two unit vectors, the more likely the result of their dot product will be 1. When the angle between two vectors is 90 degrees, the dot product becomes 0. The same applies to θθ, the larger θθ, the smaller the effect of light on the color of the fragment should be.

On the dot product of vectors

The dot product gives us a scalar that we can use to calculate the effect of light on the fragment's color. Different segments are illuminated differently depending on their orientation toward the light source.

So, what does it take to calculate diffuse lighting?

  • Normal vector: A vector perpendicular to the vertex surface.
  • Directional ray: A direction vector that is the vector difference between the position of the light source and the position of the fragment. To calculate this ray, we need the light's position vector and the fragment's position vector.

The normal vector is a (unit) vector normal to the vertex surface. Since the vertex itself has no surface (it is just an independent point in space), we use the vertices around it to calculate the surface of this vertex. We can use a little trick to calculate the normal vectors for all the vertices of the cube using cross products, but since a 3D cube is not a complex shape, we can simply add the normal data to the vertex data manually.

To calculate the diffuse light of each vertex, we need to know the following data:

  • The position of the light source: fixed value, passed in through uniform
  • The position of each vertex in the world coordinate system: multiply the Model matrix by the original coordinates of the vertex, rather than after projection and perspective conversion. This needs to be noted, because the positions of the light source and object are gl_Positionfixed, and each vertex is calculated The value of the light coming out is fixed, and it will not change with the change of projection and viewing angle.
  • The value of the (unit) normal vector of each vertex: temporarily manually filled in the vertex attributes.

Next calculate the vector from the light source to the vertex

vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);

When calculating lighting we usually don't care about the module length of a vector or its position, we only care about their direction. Therefore, almost all calculations are done using unit vectors, as this simplifies most calculations (such as dot multiplication). So when performing lighting calculations, make sure you always normalize the relevant vectors to ensure that they are truly unit vectors. Forgetting to normalize vectors is a very common mistake.

Next, we perform a dot product on normthe lightDirsum vector to calculate the actual diffuse impact of the light source on the current fragment. The resulting value is multiplied by the color of the light to obtain the diffuse component. The larger the angle between the two vectors, the smaller the diffuse component will be:

float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

Finally, add the ambient light

frag_colour = vec4((ambient + diffuse) * objectColor, 1.0);

Insert image description here

Finally, there is another problem. The coordinates of the vertices are multiplied by the Model matrix, but the normal vector is not processed. That is to say, the normal vector is not converted into the world space. The principle of this piece is more complicated. You can check it. The conclusion is :

fNormal = mat3(transpose(inverse(model))) * vNormal;

Matrix inversion is an expensive operation for the shader because it must be performed on every vertex in the scene, so inversion operations in the shader should be avoided as much as possible. This is fine for learning purposes, but for an efficient application, you'd better calculate the normal matrix on the CPU first and then pass it to the shader via a uniform (just like the model matrix).

normalModel := model1.Inv().Transpose().Mat3()

gl.UniformMatrix3fv(gl.GetUniformLocation(program1, gl.Str("normalModel\x00")), 1, false, &normalModel[0])

There is no difference in the effect, this is because we are not doing any scaling operation on the object, so we don't really need to use a normal matrix, but just multiply the normal matrix by the model matrix. But if you do unequal scaling, it is necessary to use the normal matrix to multiply the normal vector.

specular lighting

Like diffuse lighting, specular lighting also depends on the direction vector of the light and the normal vector of the object, but it also depends on the viewing direction, such as the direction from which the player is looking at the fragment. Specular lighting is determined by the reflective properties of the surface. If we imagine the surface of an object to be a mirror, then the place where specular illumination is strongest is where we see reflected light on the surface.

Insert image description here

We calculate the reflection vector by bending the direction of the incident light according to the normal vector. Then we calculate the angle difference between the reflection vector and the viewing direction. The smaller the angle between them, the greater the effect of specular light. The resulting effect is that when we look in the direction in which the incident light is reflected on the surface, we see a little highlight.

The observation vector is an additional variable we need when calculating specular lighting, we can calculate it using the world space position of the observer and the position of the fragment. We then calculate the specular intensity, multiply it by the color of the light source, and sum it with the ambient and diffuse components.

We choose to perform lighting calculations in world space, but most people tend to prefer lighting calculations in observation space. The advantage of calculating in observation space is that the observer's position is always at (0, 0, 0), so you have obtained the observer's position at zero cost. However, for learning purposes, I think it's more intuitive to calculate lighting in world space. If you still want to calculate lighting in the view space, you need to transform all relevant vectors with the view matrix as well (don't forget to modify the normal matrix as well).

To get the observer's world space coordinates, we directly use the camera's position vector.

First, we define a Specular Intensity variable to give the specular highlight a medium brightness color so that it does not have an excessive impact. If we set it to 1.0f, we get a very bright specular component, which is a bit too much for a coral-colored cube.

Next, we calculate the line of sight vector and the corresponding reflection vector along the normal axis:

vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);

Note that we lightDirnegated the vector. reflectThe function requires that the first vector be a vector from the light source to the fragment position, but lightDircurrently it is the opposite, from the fragment to the light source ( lightDirdetermined by the order of subtraction when we calculated the vector earlier). To ensure that we get the correct reflectvector, we lightDirget the opposite direction by inverting the vector. The second parameter is required to be a normal vector, so what we provide is a normalized normvector.

Calculate the specular components:

float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;

We first calculate the dot product of the sight direction and the reflection direction (and make sure it is not negative), and then raise it to the 32nd power. This 32 is the reflectivity of the highlight (Shininess). The more reflective an object is, the more capable it is of reflecting light and scattering it less, resulting in smaller highlights. In the picture below, you will see the visual impact of different reflectivity:

Insert image description here

In the early days of lighting shaders, developers used to implement the Pon's lighting model in vertex shaders. The advantage of doing lighting in a vertex shader is that it is more efficient because there are far fewer vertices than in a fragment, so (expensive) lighting calculations are done less frequently. However, the final color value in the vertex shader is just the color value of that vertex, the fragment color value is derived by interpolating the lighting color. The result is that the lighting won't look very realistic unless a large number of vertices are used.

Insert image description here

The Phong lighting model implemented in the vertex shader is called Gouraud Shading, not Phong Shading. Keep in mind that this lighting looks a bit inferior due to interpolation. Von shading produces smoother lighting effects.

Insert image description here

Complete code

package light

// 光照

import (
	"runtime"

	"github.com/go-gl/gl/v4.1-core/gl"
	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/go-gl/mathgl/mgl32"
	"github.com/phprao/go-graphic/util"
)

const (
	width  = 800
	height = 600

	vertexShaderSource = `
	#version 410

	in vec3 aPos;
	in vec3 aNormal;

	out vec3 FragPos;
	out vec3 Normal;

	uniform mat4 model;
	uniform mat4 view;
	uniform mat4 projection;
	uniform mat3 normalModel;

	void main() {
		Normal = normalModel * aNormal;
		FragPos = vec3(model * vec4(aPos, 1.0));
		gl_Position = projection * view * model * vec4(aPos, 1.0);
	}
	` + "\x00"

	fragmentShaderSource = `
	#version 410
    
	out vec4 frag_colour;

	in vec3 Normal;
	in vec3 FragPos;

	uniform vec3 lightPos;
	uniform vec3 viewPos;
	uniform vec3 lightColor;
	uniform vec3 objectColor;

	void main() {
		// 环境光
		float ambientStrength = 0.1;
		vec3 ambient = ambientStrength * lightColor;

		// 漫反射光
		vec3 norm = normalize(Normal);
		vec3 lightDir = normalize(lightPos - FragPos);
		float diff = max(dot(norm, lightDir), 0.0);
		vec3 diffuse = diff * lightColor;

		// 镜面光
		float specularStrength = 0.5;
		vec3 viewDir = normalize(viewPos - FragPos);
		vec3 reflectDir = reflect(-lightDir, norm);
		float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
		vec3 specular = specularStrength * spec * lightColor;

		frag_colour = vec4((ambient + diffuse + specular) * objectColor, 1.0);
	}
	` + "\x00"

	vertexShaderSource2 = `
	#version 410

	in vec3 aPos;

	uniform mat4 model;
	uniform mat4 view;
	uniform mat4 projection;

	void main() {
		gl_Position = projection * view * model * vec4(aPos, 1.0);
	}
	` + "\x00"

	fragmentShaderSource2 = `
	#version 410
    
	out vec4 frag_colour;

	void main() {
		frag_colour = vec4(1.0);
	}
	` + "\x00"
)

var (
	vertices = []float32{
    
    
		-0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
		0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
		0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
		0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
		-0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
		-0.5, -0.5, -0.5, 0.0, 0.0, -1.0,

		-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
		0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
		0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
		0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
		-0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
		-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,

		-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
		-0.5, 0.5, -0.5, -1.0, 0.0, 0.0,
		-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
		-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
		-0.5, -0.5, 0.5, -1.0, 0.0, 0.0,
		-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,

		0.5, 0.5, 0.5, 1.0, 0.0, 0.0,
		0.5, 0.5, -0.5, 1.0, 0.0, 0.0,
		0.5, -0.5, -0.5, 1.0, 0.0, 0.0,
		0.5, -0.5, -0.5, 1.0, 0.0, 0.0,
		0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
		0.5, 0.5, 0.5, 1.0, 0.0, 0.0,

		-0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
		0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
		0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
		0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
		-0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
		-0.5, -0.5, -0.5, 0.0, -1.0, 0.0,

		-0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
		0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
		0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
		0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
		-0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
		-0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
	}
)

func Run() {
    
    
	runtime.LockOSThread()
	window := util.InitGlfw(width, height, "light")
	defer glfw.Terminate()

	pointNum := int32(len(vertices)) / 5

	program1, _ := util.InitOpenGL(vertexShaderSource, fragmentShaderSource)
	vao1 := util.MakeVaoWithAttrib(program1, vertices, nil, []util.VertAttrib{
    
    {
    
    Name: "aPos", Size: 3}, {
    
    Name: "aNormal", Size: 3}})
	program2, _ := util.MakeProgram(vertexShaderSource2, fragmentShaderSource2)
	vao2 := util.MakeVaoWithAttrib(program2, vertices, nil, []util.VertAttrib{
    
    {
    
    Name: "aPos", Size: 3}, {
    
    Name: "aNormal", Size: 3}})

	gl.ClearColor(0.1, 0.1, 0.1, 1.0)
	gl.Enable(gl.DEPTH_TEST)

	lightPos := mgl32.Vec3{
    
    2, 0, 0}
	lightColor := mgl32.Vec3{
    
    1, 1, 1}
	objectColor := mgl32.Vec3{
    
    1, 0.5, 0.31}

	cameraPos := mgl32.Vec3{
    
    0, 0, 3}
	cameraFront := mgl32.Vec3{
    
    0, 0, -1}
	cameraUp := mgl32.Vec3{
    
    0, 1, 0}

	camera := util.NewCamera(cameraPos, cameraFront, cameraUp, width, height)
	camera.SetCursorPosCallback(window)

	for !window.ShouldClose() {
    
    
		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
        
         // lightPos = mgl32.Vec3{2, float32(math.Sin(glfw.GetTime())), 0}

		// 画箱子
		gl.UseProgram(program1)

		gl.Uniform3fv(gl.GetUniformLocation(program1, gl.Str("lightColor\x00")), 1, &lightColor[0])
		gl.Uniform3fv(gl.GetUniformLocation(program1, gl.Str("lightPos\x00")), 1, &lightPos[0])
		gl.Uniform3fv(gl.GetUniformLocation(program1, gl.Str("objectColor\x00")), 1, &objectColor[0])
		gl.Uniform3fv(gl.GetUniformLocation(program1, gl.Str("viewPos\x00")), 1, &camera.CameraPos[0])
		view := camera.LookAt()
		projection := camera.Perspective()

		model1 := mgl32.Ident4()
		normalModel := model1.Inv().Transpose().Mat3()
		gl.UniformMatrix4fv(gl.GetUniformLocation(program1, gl.Str("model\x00")), 1, false, &model1[0])
		gl.UniformMatrix4fv(gl.GetUniformLocation(program1, gl.Str("view\x00")), 1, false, &view[0])
		gl.UniformMatrix4fv(gl.GetUniformLocation(program1, gl.Str("projection\x00")), 1, false, &projection[0])
		gl.UniformMatrix3fv(gl.GetUniformLocation(program1, gl.Str("normalModel\x00")), 1, false, &normalModel[0])
		gl.BindVertexArray(vao1)
		gl.DrawArrays(gl.TRIANGLES, 0, pointNum)

		// 画光源
		gl.UseProgram(program2)
		model2 := mgl32.Translate3D(lightPos.X(), lightPos.Y(), lightPos.Z()).Mul4(mgl32.Scale3D(0.2, 0.2, 0.2))
		gl.UniformMatrix4fv(gl.GetUniformLocation(program2, gl.Str("model\x00")), 1, false, &model2[0])
		gl.UniformMatrix4fv(gl.GetUniformLocation(program2, gl.Str("view\x00")), 1, false, &view[0])
		gl.UniformMatrix4fv(gl.GetUniformLocation(program2, gl.Str("projection\x00")), 1, false, &projection[0])
		gl.BindVertexArray(vao2)
		gl.DrawArrays(gl.TRIANGLES, 0, pointNum)

		glfw.PollEvents()
		window.SwapBuffers()
	}
}

We can also dynamically move the light source and observe it, which will help us understand the Feng lighting model.

To summarize: ambient light is easy to understand; diffuse light is determined by the relative positions of the object and the light source; specular light is determined by the relative positions of the object, light source, and camera.

Material

When describing a surface, we can define a Material Color for three lighting components: Ambient Lighting, Diffuse Lighting and Specular Lighting. By assigning a color to each component, we can have fine-grained control over the color output of the surface. Now, let's add another shininess component, and combined with the three colors above, we have all the required material properties:

struct Material {
    
    
    vec3 ambient; // 环境光颜色
    vec3 diffuse; // 漫反射光颜色
    vec3 specular; // 镜面光颜色
    float shininess; // 镜面光的反光度
}; 

uniform Material material;

We define a color vector for each component of the Feng lighting model. The ambient material vector defines what color this surface reflects under ambient lighting, and is usually the same as the surface's color. The diffuse material vector defines the color of the surface under diffuse lighting. The diffuse color (like the ambient lighting) is also set to the desired object color. The specular material vector sets the color of the specular highlight on the surface (or may even reflect the color of a specific surface). Finally, shininess affects the scattering/radius of specular highlights.

A table in devernay.free.fr shows a series of material properties that simulate real materials in the real world.

Figuring out the correct material settings for an object is a difficult project, which mainly requires experimentation and extensive experience. It often happens that the visual quality of an object is ruined by using inappropriate materials.

After we have the material color, we no longer need to pass in the color of the object.

#version 410

out vec4 frag_colour;

in vec3 Normal;
in vec3 FragPos;

uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;

struct Material {
    
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
}; 

uniform Material material;

void main() {
    
    
    // 环境光
    vec3 ambient = lightColor * material.ambient;

    // 漫反射光
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = lightColor * diff * material.diffuse;

    // 镜面光
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = lightColor * spec * material.specular;

    frag_colour = vec4(ambient + diffuse + specular, 1.0);
}

Pass value

gl.Uniform3f(gl.GetUniformLocation(program1, gl.Str("material.ambient\x00")), 1, 0.5, 0.31)
gl.Uniform3f(gl.GetUniformLocation(program1, gl.Str("material.diffuse\x00")), 1, 0.5, 0.31)
gl.Uniform3f(gl.GetUniformLocation(program1, gl.Str("material.specular\x00")), 0.5, 0.5, 0.5)
gl.Uniform1f(gl.GetUniformLocation(program1, gl.Str("material.shininess\x00")), 32)

Insert image description here

Properties of light

The objects above are too bright. We also need to set colors for ambient light, diffuse light, and specular light respectively.

#version 410
    
out vec4 frag_colour;

in vec3 Normal;
in vec3 FragPos;

uniform vec3 viewPos;

struct Material {
    
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
}; 

uniform Material material;

struct Light {
    
    
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Light light;

void main() {
    
    
    // 环境光
    vec3 ambient = light.ambient * material.ambient;

    // 漫反射光
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * material.diffuse;

    // 镜面光
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * spec * material.specular;

    frag_colour = vec4(ambient + diffuse + specular, 1.0);
}
gl.Uniform3f(gl.GetUniformLocation(program1, gl.Str("light.ambient\x00")), 0.2, 0.2, 0.2)
gl.Uniform3f(gl.GetUniformLocation(program1, gl.Str("light.diffuse\x00")), 0.5, 0.5, 0.5)
gl.Uniform3f(gl.GetUniformLocation(program1, gl.Str("light.specular\x00")), 1, 1, 1)
gl.Uniform3fv(gl.GetUniformLocation(program1, gl.Str("light.position\x00")), 1, &lightPos[0])

Insert image description here

Different light source colors

Above we have separated the various components of light. We can change the color of light by changing these components to achieve different effects.

lightmap

It’s the effect of texture mapping and lighting.

Diffuse map

Here, the Diffuse Map will be used to replace the diffuse light. It is a texture image that represents all the diffuse colors of an object.

Using the diffuse map in the shader is exactly the same as in the texture tutorial. But this time we will store the texture as a Material structure sampler2D. We replace the previously defined vec3diffuse color vector with a diffuse map.

We also removed the ambient material color vector, because the ambient color is equal to the diffuse color in almost all cases, so we don't need to store them separately, don't forget to set the ambient material color to the diffuse material color as well value.

Insert image description here

specular map

Just add the specular light map.

We believe that the wood in the middle of the box will not reflect light, so we set the wood part to black in the second map.

Below is the effect of adding diffuse and specular maps.

Insert image description here

light source
1. Parallel light/directional light

When a light source is far away, each ray from the light source will be approximately parallel to each other. When we use a model that assumes the light source is at infinity , it is called a directional light because all its rays have the same direction and it has nothing to do with the position of the light source. A great example of directional light is the sun.

Insert image description here

Because all rays are parallel, the relative position of the object to the light source is unimportant because the direction of the light is the same for every object in the scene. Since the light's position vector remains consistent, the lighting calculations for each object in the scene will be similar.

We can define a light direction vector instead of a position vector to simulate a directional light. The calculation of the shader remains basically the same, but this time we will use the direction vector of the light directly instead of calculating the lightDir vector through position.

#version 410

......

struct Light {
    
    
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Light light;

void main() {
    
    
    ......
    vec3 lightDir = normalize(-light.direction);
    ......
    frag_colour = vec4(ambient + diffuse + specular, 1.0);
}
// 取反之后为照射方向,也就是说此时是从 Z=1 照射到 z=0
lightDirection := mgl32.Vec3{
    
    0, 0, -1}

We let the direction of the illuminating light change dynamically

lightDirection = mgl32.Vec3{
    
    0, float32(math.Sin(glfw.GetTime())), -1}

Insert image description here

2. Point light source

Directional lights are great for global lights that illuminate the entire scene, but in addition to directional lights we also need some point lights scattered throughout the scene. A point light is a light source located at a certain location in the world that emits light in all directions, but the light gradually decays with distance. Think of light bulbs and torches as light-cast objects. They are both point sources of light.

Insert image description here

In previous tutorials we have been using a (simplified) point light. We have a light source at a given location that scatters light in all directions starting from its light source position. However, the light source we define simulates light that never decays, which looks like the light source is very bright. In most 3D simulations, we want the simulated light source to illuminate only the area near the light source rather than the entire scene.

If you add 10 boxes to the lighting scene in the previous section, you will notice that the box at the back and the box in front of the light are illuminated with the same intensity, and there is no defined formula to attenuate the light with distance. . We want the boxes in the back row to be only slightly lit compared to the boxes in the front row.

attenuation

As the light propagation distance increases, the gradual reduction of light intensity is usually called attenuation. One way to reduce light intensity with distance is to use a linear equation. Such an equation would linearly reduce the intensity of light with distance, making distant objects dimmer. However, such linear equations often look artificial. In the real world, lights are usually very bright when close, but as distance increases, the brightness of the light source will initially drop very quickly, and then the remaining light intensity will drop very slowly as the distance increases. So, we need a different formula to reduce the intensity of the light.

Insert image description here

Insert image description here

dRepresents distance, and other parameters are coefficient settings.

Insert image description here

3. Concentration

The last type of light we'll discuss is Spotlight. A spot light is a light source located somewhere in the environment that shines light in only one specific direction rather than in all directions. The result is that only objects within a specific radius in the direction of the focused light will be illuminated, while other objects will remain dark. Good examples of spotlights are street lamps or flashlights.

Guess you like

Origin blog.csdn.net/raoxiaoya/article/details/131429533