LWJGL教程04 - 渲染

渲染管线

在写代码之前,先了解openGL的渲染管线。

渲染管线

顶点数据

顾名思义,三角形各个顶点的数据。

顶点着色器

通过坐标变换,将顶点数据变换到需要的坐标系下。

图元装配

将顶点组装成点,线,面等。

几何着色器

细分着色器

光栅化

将顶点数据转化为像素。

片段着色器

将像素进行最后的着色。

测试与混合

创建着色器

先来创建顶点着色器vertex.vert

1
2
3
4
5
6
7
8
#version 330

layout (location=0) in vec3 position;

void main()
{
gl_Position = vec4(position, 1.0);
}

再来创建片段着色器fragment.frag

1
2
3
4
5
6
7
8
#version 330

out vec4 fragColor;

void main()
{
fragColor = vec4(0.0, 0.5, 0.5, 1.0);
}

使用着色器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
[imports ...]

public class ShaderProgram {

private final int programId;

private int vertexShaderId;

private int fragmentShaderId;

public ShaderProgram() throws RuntimeException {
programId = glCreateProgram();
if (programId == 0) {
throw new RuntimeException("Could not create Shader");
}
}

public void createVertexShader(String shaderCode) {
vertexShaderId = createShader(shaderCode, GL_VERTEX_SHADER);
}

public void createFragmentShader(String shaderCode) {
fragmentShaderId = createShader(shaderCode, GL_FRAGMENT_SHADER);
}

protected int createShader(String shaderCode, int shaderType) {
int shaderId = glCreateShader(shaderType);
if (shaderId == 0) {
throw new RuntimeException("Error creating shader. Type: " + shaderType);
}

glShaderSource(shaderId, shaderCode);
glCompileShader(shaderId);

if (glGetShaderi(shaderId, GL_COMPILE_STATUS) == 0) {
throw new RuntimeException("Error compiling Shader code: " + glGetShaderInfoLog(shaderId, 1024));
}

glAttachShader(programId, shaderId);

return shaderId;
}

public void link() throws RuntimeException {
glLinkProgram(programId);
if (glGetProgrami(programId, GL_LINK_STATUS) == 0) {
throw new RuntimeException("Error linking Shader code: " + glGetProgramInfoLog(programId, 1024));
}

if (vertexShaderId != 0) {
glDetachShader(programId, vertexShaderId);
}
if (fragmentShaderId != 0) {
glDetachShader(programId, fragmentShaderId);
}

glValidateProgram(programId);
if (glGetProgrami(programId, GL_VALIDATE_STATUS) == 0) {
System.err.println("Warning validating Shader code: " + glGetProgramInfoLog(programId, 1024));
}

}

public void bind() {
glUseProgram(programId);
}

public void unbind() {
glUseProgram(0);
}

public void cleanup() {
unbind();
if (programId != 0) {
glDeleteProgram(programId);
}
}
}

还需要一个类来读取文件。

1
2
3
4
5
6
7
8
9
10
11
[imports ...]

public class Utils {
public static String readFile(String filepath) {
try {
return Files.readString(Paths.get(filepath));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

Render中添加ShaderProgram

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[imports ...]

public class Render {
public static ShaderProgram shaderProgram;

public static void createRender() {
[...]
shaderProgram = new ShaderProgram();
shaderProgram.createVertexShader(Utils.readFile("src/main/resources/vertex.vert"));
shaderProgram.createFragmentShader(Utils.readFile("src/main/resources/fragment.frag"));
shaderProgram.link();
}

public static void cleanup() {
shaderProgram.cleanup();
}

[...]
}

现在尝试绘制一点东西。

首先修改ILogic

1
2
3
4
5
6
public interface ILogic {
void init();
void input();
void render();
void cleanup();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
[imports ...]

public class TestLogic implements ILogic {
float[] vertices;

@Override
public void init() {
vertices = new float[]{
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};

FloatBuffer verticesBuffer = MemoryUtil.memAllocFloat(vertices.length);
verticesBuffer.put(vertices).flip();
vaoId = glGenVertexArrays();
glBindVertexArray(vaoId);
vboId = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
// 解绑VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 解绑VAO
glBindVertexArray(0);
MemoryUtil.memFree(verticesBuffer);
}

int vaoId;
int vboId;

@Override
public void render() {

Render.shaderProgram.bind();

// 绑定VAO
glBindVertexArray(vaoId);

// 绘制顶点
glDrawArrays(GL_TRIANGLES, 0, 3);

// 还原状态
glBindVertexArray(0);

Render.shaderProgram.unbind();
}

@Override
public void input() {
if (Window.isKeyPressed(GLFW.GLFW_KEY_SPACE))
System.out.println("Space is pressed");
}

@Override
public void cleanup() {

glDisableVertexAttribArray(0);

// 删除VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(vboId);

// 删除VAO
glBindVertexArray(0);
glDeleteVertexArrays(vaoId);
}
}

还要修改Engine类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.game;

public class Engine {
[...]

public void run() {
[...]

logic.init();
Render.createRender();

[...]
while(!Window.windowShouldClose()) {
[...]

Render.render();

logic.render();

[...]
}
[...]
}

private void cleanup() {
logic.cleanup();
Render.cleanup();
Window.cleanup();
}
}

测试一下。

一个三角形 - 运行结果

经过努力,终于绘制出了第一个三角形。

但是代码写的杂乱无章,还需要修改。

之后将会改进代码。


LWJGL教程04 - 渲染
https://panxy02.github.io/2024/07/18/lwjgl-04/
作者
52Hertz
发布于
2024年7月18日
许可协议