无法在 lwjgl 中使用 vbos opengl 绘制基本三角形
Cannot draw basic triangle with vbos opengl in lwjgl
我尝试使用 lwjgl 绘制一个简单的三角形
这是我的 3D 对象 class:
public class ThreeDObject {
float[] vertices;
float[] color;
int vao;
int vvbo; //vertex vbo
int cvbo; //color vbo
int vtexNumber;
boolean isSetUp = false;
String fragmentShader;
String vertexShader;
int frag;
int vert;
int program;
public ThreeDObject( float[] vertices, float[] color) {
this.vertices = vertices;
this.color = color;
this.vtexNumber = this.vertices.length / 3;
setupVAO();
this.vertexShader = defaultVert;
this.fragmentShader = defaultFrag;
setupShaders();
System.out.println(Arrays.toString(this.vertices));
}
public void setupVAO() {
FloatBuffer vb = BufferUtils.createFloatBuffer(vertices.length);
vb.put(vertices).flip();
FloatBuffer cb = BufferUtils.createFloatBuffer(color.length);
cb.put(color).flip();
if(hasEBO()) {
IntBuffer ob = BufferUtils.createIntBuffer(order.length);
ob.put(order).flip();
}
vao = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vao);
vvbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vvbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vb, GL15.GL_STATIC_DRAW);
GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL20.glEnableVertexAttribArray(0);
GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 0, 0);
cvbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, cvbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, cb, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL20.glEnableVertexAttribArray(1);
GL30.glVertexAttribPointer(1, 3, GL30.GL_FLOAT, false, 0, 0);
GL30.glBindVertexArray(0);
isSetUp = true;
}
public void setupShaders() {
GL30.glBindVertexArray(vao);
program = GL20.glCreateProgram();
vert = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
GL20.glShaderSource(vert, vertexShader);
frag = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
GL20.glShaderSource(frag, fragmentShader);
GL20.glCompileShader(vert);
if(GL20.glGetShaderi(vert, GL20.GL_COMPILE_STATUS) == GL20.GL_FALSE) {
System.out.println("Couldn't comile vertex shader");
System.out.println(GL30.glGetShaderInfoLog(vert));
}
GL20.glCompileShader(frag);
if(GL20.glGetShaderi(frag, GL20.GL_COMPILE_STATUS) == GL20.GL_FALSE) {
System.out.println("Couldn't comile fragment shader");
System.out.println(GL30.glGetShaderInfoLog(frag));
}
GL20.glAttachShader(program, vert);
GL20.glAttachShader(program, frag);;
GL20.glValidateProgram(program);
if(GL20.glGetProgrami(program, GL20.GL_VALIDATE_STATUS) != GL20.GL_TRUE) {
System.out.println("Can't validate shader");
System.out.println(GL20.glGetProgramInfoLog(program));
}
GL30.glBindVertexArray(0);
}
public void draw() {
GL20.glUseProgram(program);
GL30.glBindVertexArray(vao);
GL15.glDrawArrays(GL15.GL_TRIANGLES, 0, vtexNumber);
GL30.glBindVertexArray(0);
}
}
我的构造函数是:
t = new ThreeDObject(new float[] {
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f
}, new float[] {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
});
(构造函数是故意错的)
我删除了一些未使用的代码,如果您找不到错误请告诉我
我得到的只是一个白色三角形被绘制到屏幕上。
我尝试了我能找到的所有解决方案,如果你能帮助我,我将不胜感激。
编辑:我更改了一些代码但仍然得到相同的结果。
在控制台上我得到这个:
Can't validate shader
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, 0.0]
这是顶点着色器:
#version 330
out vec4 outColor;
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inColor;
void main() {
gl_Position = vec4(inPosition,1.0);
outColor = vec4(inColor, 1.0);
}
这是片段着色器:
#version 330
in vec3 outColor;
out vec4 finalColor;
void main() {
finalColor = vec4(1.0,0.0,0.0,1.0);
}
结果是这个三角形:
Image
当我 link 着色器时的结果:
Result
编辑 2:
我改了密码
我正在 link 着色器:
GL20.glAttachShader(program, frag);
GL20.glLinkProgram(program);
if(GL20.glGetProgrami(program, GL20.GL_LINK_STATUS) != GL20.GL_TRUE) {
System.out.println("Can't link shader");
System.out.println(GL20.glGetProgramInfoLog(program));
}
GL20.glValidateProgram(program);
我也更改了这一行:
GL15.glDrawArrays(GL15.GL_TRIANGLES, 0, vtexNumber);
GL30.glBindVertexArray(0);
GL20.glUseProgram(0);
并更改了顶点和片段着色器:
Vertex:
#version 330
out vec3 vertColor;
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inColor;
void main() {
gl_Position = vec4(inPosition,1.0);
vertColor = inColor;
}
Fragment:
#version 330
in vec3 vertColor;
out vec4 FragColor;
void main() {
FragColor = vec4(vertColor,1.0);
}
但我仍然得到奇怪的结果:
我得到一个黑色三角形覆盖了一半 window,我尝试移动相机但没有任何变化。
Black triangle
首先,在调用GL20.glEnableVertexAttribArray(1);
之前调用GL30.glVertexAttribPointer(1, 3, GL30.GL_FLOAT, false, 0, 0);
(用于颜色缓冲区)(您应该在启用属性数组之前分配您的属性)。
其次,你的绘制函数不正确:
渲染时,
绑定你的着色器(GL20.glUseProgram(program)
)
绑定您的 VAO (GL30.glBindVertexArray(VAO)
)
设置着色器制服(我猜你还没有完成这一步,所以暂时不要担心)
启用顶点属性数组 (GL30.glEnableVertexAttribArray(number)
) - 为分配的每个缓冲区槽和每次渲染调用此方法(不仅仅是在设置缓冲区时)。
渲染/调用绘制函数
禁用顶点属性数组 (GL30.glDisableVertexAttribArray(number)
) - 为分配的每个缓冲区槽调用此方法,并在每次渲染时再次调用此方法
解除绑定 VAO (GL30.glBindVertexArray(0)
)
取消绑定着色器(GL20.glUseProgram(0)
)
好的,我测试了你的代码,发现了 2 个错误,
1. 因为我怀疑你的三角形顶点数据排列不正确,
当我测试你的顶点数据时,我得到一个空白屏幕(我知道你是如何设法得到一个三角形的)。
t = new ThreeDObject(new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f }, // Your vertex data
结果
A blank screen (grey is the clear colour)
如果你看到,你的顶点数据没有按逆时针顺序(CCW)排列; OpenGL 更喜欢 CCW 顺序的数据。应该是这样的:
Sorry for the link, cannot embed photo's yet, btw look at triangle 2, for counter-clockwise vertex order
所以,我将订单更改为:
t = new ThreeDObject(new float[] { -1.0f, 1.0f, 0f, -1.0f, -1.0f, 0f, 1.0f, -1.0f, 0f } //V1 --> V3 --> V2
导致:
The triangle you hopefully wanted (with the colours)
Here's another one (with a every coordinate halved)
2. 还有这个代码:
cvbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, cvbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, cb, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL20.glEnableVertexAttribArray(1);
GL30.glVertexAttribPointer(1, 3, GL30.GL_FLOAT, false, 0, 0);
应该是这样的:
cvbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, cvbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, cb, GL15.GL_STATIC_DRAW);
GL30.glVertexAttribPointer(1, 3, GL30.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL20.glEnableVertexAttribArray(1);
始终在绑定缓冲区时分配您的属性槽,因此出现错位错误的可能性很小(这是您可能得到黑色三角形的原因,因为您的数据混淆了)。
如果您需要更多帮助或需要查看我的代码,请发表评论!
致其他人:这不是问题的答案,而是作者在另一个答案中的评论。
ViewMatrix:
定义相机时,从技术上讲/需要定义视图 space(因此称为 viewMatrix or lookAtMatrix
)。这很重要,因为此矩阵将您的对象从世界 space 转换为视图 space(相机 space),其中所有对象都是根据相机定义的。
(想一想...如果您的 worldMatrix / modelMatrix
根据世界原点变换所有对象,那么您的 viewMatrix
根据相机变换所有对象(相机现在是原点的地方,相机没有看到的任何东西都会被自动丢弃)。
这就是我在我的程序中定义相机 class 的方式(一直滚动到最后以进行完整细分):
public class Camera {
private Vector3f position;
private Vector3f cameraDirection;
private float pitch = 0f;
private float yaw = -90.0f;
private float roll = 0f;
private float lastMX = 0f;
private float lastMY = 0f;
private float mouseSensitivity = 0.1f;
private Vector3f front = new Vector3f(0, 0 ,-1);
private Vector3f up = new Vector3f(0, 1, 0);
private Matrix4f lookAt;
public Camera(Vector3f position) {
this.position = position;
lookAt = new Matrix4f().lookAt(position, position.add(front), up);
}
public void input() {
float xOffSet = (float) (Input.getMouseX()) - lastMX;
float yOffSet = (float) (-Input.getMouseY()) + lastMY;
lastMX = (float) Input.getMouseX();
lastMY = (float) Input.getMouseY();
yaw += (xOffSet *= mouseSensitivity);
pitch += (yOffSet *= mouseSensitivity);
if(pitch > 89.0f) pitch = 89.0f;
if(pitch < -89.0f) pitch = -89.0f;
float cameraSpeed = 2.5f * Window.getDeltaTime();
if(Input.isKeyDown(GLFW.GLFW_KEY_W)) position.add(front.mul(cameraSpeed));
if(Input.isKeyDown(GLFW.GLFW_KEY_S)) position.sub(front.mul(cameraSpeed));
if(Input.isKeyDown(GLFW.GLFW_KEY_A)) position.sub(new Vector3f(front.cross(up)).normalize().mul(cameraSpeed));
if(Input.isKeyDown(GLFW.GLFW_KEY_D)) position.add(new Vector3f(front.cross(up)).normalize().mul(cameraSpeed));
position.y = 0.0f;
}
public void update() {
input();
setCameraDirection(new Vector3f((float) Math.cos(Math.toRadians(yaw)),
(float) Math.sin(Math.toRadians(pitch)),
(float) Math.sin(Math.toRadians(yaw))));
front = getCameraDirection().normalize();
lookAt.identity().lookAt(position, new Vector3f(position).add(front), up);
}
public Vector3f getPosition() {
return position;
}
public float getPitch() {
return pitch;
}
public float getYaw() {
return yaw;
}
public float getRoll() {
return roll;
}
public Vector3f getCameraDirection() {
return cameraDirection;
}
public void setCameraDirection(Vector3f cameraDirection) {
this.cameraDirection = cameraDirection;
}
public Matrix4f getLookAtMatrix() {
return lookAt;
}
}
因此,如果您已经看到 lookAt
矩阵是使用 JOML 由三个向量定义的:
一个Camera Position
;
A Center (Front Vector + Camera Position)
;
和一个 Up
向量;
Camera Position
简单地定义了相机在世界中的位置(显然它会如何存在)。
Center
有点难以理解,但它所做的一切都定义了 相机应该注视的点 。
如果你的中心不变,那么你就得到了所谓的Third Person Camera
,其中相机以可变半径旋转大约一个center
点。
但是如果你想要First Person Camera
,你的center
不能保持不变,因此必须每帧重新计算能够创造出流畅的第一人称效果。
如果见上文,在 update()
方法中,front
向量使用 cameraDirection
(相机面对的方向)在每一帧重新计算。
cameraDirection
本身必须计算每一帧才能使上述过程正常工作。
俯仰、偏航、滚转:
What are they
它们只是围绕三个轴的旋转 (x = Pitch, y = Yaw, z = Roll)
(见图)。使用这些和一些三角函数,我们可以计算出相机的朝向 (cameraDirection
):
public void update() {
input();
setCameraDirection(new Vector3f((float) Math.cos(Math.toRadians(yaw)),
(float) Math.sin(Math.toRadians(pitch)),
(float) Math.sin(Math.toRadians(yaw))));
front = getCameraDirection().normalize();
lookAt.identity().lookAt(position, new Vector3f(position).add(front), up);
}
We can calculate x and z components of the direction vector, using the y rotation (YAW) and some trigonometry
We also need the y direction, which can be calculate through the (PITCH)
确保将方向向量(与任何方向向量一样)归一化为单位向量,否则中心将被弄乱
所以现在我们有一个 direction / front
和 position
向量。
UP
向量:
要像 view space
或 world space
或 tangent space
一样定义 "space"
,您需要三个向量来定义 space 的三个轴 - -> x轴、y轴和z轴
我对此了解不深(我可能是错的):
UP
向量定义视图space的y轴(x轴是方向向量,z轴(右向量)是x轴和y轴的叉积)。
UP
向量只是 (0, 1, 0)
并且永远不会改变,因为 1
表示它是直线向上(正 y 方向)。
还有 lookAt
矩阵函数:
//camPos //center //up
lookAt = new Matrix4f().lookAt(position, position.add(front), up);
制作人员:https://learnopengl.com/Getting-started/Camera、
绝对推荐查看本教程以获得深入的教程。
我尝试使用 lwjgl 绘制一个简单的三角形
这是我的 3D 对象 class:
public class ThreeDObject {
float[] vertices;
float[] color;
int vao;
int vvbo; //vertex vbo
int cvbo; //color vbo
int vtexNumber;
boolean isSetUp = false;
String fragmentShader;
String vertexShader;
int frag;
int vert;
int program;
public ThreeDObject( float[] vertices, float[] color) {
this.vertices = vertices;
this.color = color;
this.vtexNumber = this.vertices.length / 3;
setupVAO();
this.vertexShader = defaultVert;
this.fragmentShader = defaultFrag;
setupShaders();
System.out.println(Arrays.toString(this.vertices));
}
public void setupVAO() {
FloatBuffer vb = BufferUtils.createFloatBuffer(vertices.length);
vb.put(vertices).flip();
FloatBuffer cb = BufferUtils.createFloatBuffer(color.length);
cb.put(color).flip();
if(hasEBO()) {
IntBuffer ob = BufferUtils.createIntBuffer(order.length);
ob.put(order).flip();
}
vao = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vao);
vvbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vvbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vb, GL15.GL_STATIC_DRAW);
GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL20.glEnableVertexAttribArray(0);
GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 0, 0);
cvbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, cvbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, cb, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL20.glEnableVertexAttribArray(1);
GL30.glVertexAttribPointer(1, 3, GL30.GL_FLOAT, false, 0, 0);
GL30.glBindVertexArray(0);
isSetUp = true;
}
public void setupShaders() {
GL30.glBindVertexArray(vao);
program = GL20.glCreateProgram();
vert = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
GL20.glShaderSource(vert, vertexShader);
frag = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
GL20.glShaderSource(frag, fragmentShader);
GL20.glCompileShader(vert);
if(GL20.glGetShaderi(vert, GL20.GL_COMPILE_STATUS) == GL20.GL_FALSE) {
System.out.println("Couldn't comile vertex shader");
System.out.println(GL30.glGetShaderInfoLog(vert));
}
GL20.glCompileShader(frag);
if(GL20.glGetShaderi(frag, GL20.GL_COMPILE_STATUS) == GL20.GL_FALSE) {
System.out.println("Couldn't comile fragment shader");
System.out.println(GL30.glGetShaderInfoLog(frag));
}
GL20.glAttachShader(program, vert);
GL20.glAttachShader(program, frag);;
GL20.glValidateProgram(program);
if(GL20.glGetProgrami(program, GL20.GL_VALIDATE_STATUS) != GL20.GL_TRUE) {
System.out.println("Can't validate shader");
System.out.println(GL20.glGetProgramInfoLog(program));
}
GL30.glBindVertexArray(0);
}
public void draw() {
GL20.glUseProgram(program);
GL30.glBindVertexArray(vao);
GL15.glDrawArrays(GL15.GL_TRIANGLES, 0, vtexNumber);
GL30.glBindVertexArray(0);
}
}
我的构造函数是:
t = new ThreeDObject(new float[] {
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f
}, new float[] {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
});
(构造函数是故意错的)
我删除了一些未使用的代码,如果您找不到错误请告诉我
我得到的只是一个白色三角形被绘制到屏幕上。
我尝试了我能找到的所有解决方案,如果你能帮助我,我将不胜感激。
编辑:我更改了一些代码但仍然得到相同的结果。
在控制台上我得到这个:
Can't validate shader
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, 0.0]
这是顶点着色器:
#version 330
out vec4 outColor;
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inColor;
void main() {
gl_Position = vec4(inPosition,1.0);
outColor = vec4(inColor, 1.0);
}
这是片段着色器:
#version 330
in vec3 outColor;
out vec4 finalColor;
void main() {
finalColor = vec4(1.0,0.0,0.0,1.0);
}
结果是这个三角形:
Image
当我 link 着色器时的结果:
Result
编辑 2:
我改了密码
我正在 link 着色器:
GL20.glAttachShader(program, frag);
GL20.glLinkProgram(program);
if(GL20.glGetProgrami(program, GL20.GL_LINK_STATUS) != GL20.GL_TRUE) {
System.out.println("Can't link shader");
System.out.println(GL20.glGetProgramInfoLog(program));
}
GL20.glValidateProgram(program);
我也更改了这一行:
GL15.glDrawArrays(GL15.GL_TRIANGLES, 0, vtexNumber);
GL30.glBindVertexArray(0);
GL20.glUseProgram(0);
并更改了顶点和片段着色器:
Vertex:
#version 330
out vec3 vertColor;
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inColor;
void main() {
gl_Position = vec4(inPosition,1.0);
vertColor = inColor;
}
Fragment:
#version 330
in vec3 vertColor;
out vec4 FragColor;
void main() {
FragColor = vec4(vertColor,1.0);
}
但我仍然得到奇怪的结果:
我得到一个黑色三角形覆盖了一半 window,我尝试移动相机但没有任何变化。
Black triangle
首先,在调用GL20.glEnableVertexAttribArray(1);
之前调用GL30.glVertexAttribPointer(1, 3, GL30.GL_FLOAT, false, 0, 0);
(用于颜色缓冲区)(您应该在启用属性数组之前分配您的属性)。
其次,你的绘制函数不正确:
渲染时,
绑定你的着色器(
GL20.glUseProgram(program)
)绑定您的 VAO (
GL30.glBindVertexArray(VAO)
)设置着色器制服(我猜你还没有完成这一步,所以暂时不要担心)
启用顶点属性数组 (
GL30.glEnableVertexAttribArray(number)
) - 为分配的每个缓冲区槽和每次渲染调用此方法(不仅仅是在设置缓冲区时)。渲染/调用绘制函数
禁用顶点属性数组 (
GL30.glDisableVertexAttribArray(number)
) - 为分配的每个缓冲区槽调用此方法,并在每次渲染时再次调用此方法解除绑定 VAO (
GL30.glBindVertexArray(0)
)取消绑定着色器(
GL20.glUseProgram(0)
)
好的,我测试了你的代码,发现了 2 个错误,
1. 因为我怀疑你的三角形顶点数据排列不正确, 当我测试你的顶点数据时,我得到一个空白屏幕(我知道你是如何设法得到一个三角形的)。
t = new ThreeDObject(new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f }, // Your vertex data
结果
A blank screen (grey is the clear colour)
如果你看到,你的顶点数据没有按逆时针顺序(CCW)排列; OpenGL 更喜欢 CCW 顺序的数据。应该是这样的:
Sorry for the link, cannot embed photo's yet, btw look at triangle 2, for counter-clockwise vertex order
所以,我将订单更改为:
t = new ThreeDObject(new float[] { -1.0f, 1.0f, 0f, -1.0f, -1.0f, 0f, 1.0f, -1.0f, 0f } //V1 --> V3 --> V2
导致:
The triangle you hopefully wanted (with the colours)
Here's another one (with a every coordinate halved)
2. 还有这个代码:
cvbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, cvbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, cb, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL20.glEnableVertexAttribArray(1);
GL30.glVertexAttribPointer(1, 3, GL30.GL_FLOAT, false, 0, 0);
应该是这样的:
cvbo = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, cvbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, cb, GL15.GL_STATIC_DRAW);
GL30.glVertexAttribPointer(1, 3, GL30.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL20.glEnableVertexAttribArray(1);
始终在绑定缓冲区时分配您的属性槽,因此出现错位错误的可能性很小(这是您可能得到黑色三角形的原因,因为您的数据混淆了)。
如果您需要更多帮助或需要查看我的代码,请发表评论!
致其他人:这不是问题的答案,而是作者在另一个答案中的评论。
ViewMatrix:
定义相机时,从技术上讲/需要定义视图 space(因此称为 viewMatrix or lookAtMatrix
)。这很重要,因为此矩阵将您的对象从世界 space 转换为视图 space(相机 space),其中所有对象都是根据相机定义的。
(想一想...如果您的 worldMatrix / modelMatrix
根据世界原点变换所有对象,那么您的 viewMatrix
根据相机变换所有对象(相机现在是原点的地方,相机没有看到的任何东西都会被自动丢弃)。
这就是我在我的程序中定义相机 class 的方式(一直滚动到最后以进行完整细分):
public class Camera {
private Vector3f position;
private Vector3f cameraDirection;
private float pitch = 0f;
private float yaw = -90.0f;
private float roll = 0f;
private float lastMX = 0f;
private float lastMY = 0f;
private float mouseSensitivity = 0.1f;
private Vector3f front = new Vector3f(0, 0 ,-1);
private Vector3f up = new Vector3f(0, 1, 0);
private Matrix4f lookAt;
public Camera(Vector3f position) {
this.position = position;
lookAt = new Matrix4f().lookAt(position, position.add(front), up);
}
public void input() {
float xOffSet = (float) (Input.getMouseX()) - lastMX;
float yOffSet = (float) (-Input.getMouseY()) + lastMY;
lastMX = (float) Input.getMouseX();
lastMY = (float) Input.getMouseY();
yaw += (xOffSet *= mouseSensitivity);
pitch += (yOffSet *= mouseSensitivity);
if(pitch > 89.0f) pitch = 89.0f;
if(pitch < -89.0f) pitch = -89.0f;
float cameraSpeed = 2.5f * Window.getDeltaTime();
if(Input.isKeyDown(GLFW.GLFW_KEY_W)) position.add(front.mul(cameraSpeed));
if(Input.isKeyDown(GLFW.GLFW_KEY_S)) position.sub(front.mul(cameraSpeed));
if(Input.isKeyDown(GLFW.GLFW_KEY_A)) position.sub(new Vector3f(front.cross(up)).normalize().mul(cameraSpeed));
if(Input.isKeyDown(GLFW.GLFW_KEY_D)) position.add(new Vector3f(front.cross(up)).normalize().mul(cameraSpeed));
position.y = 0.0f;
}
public void update() {
input();
setCameraDirection(new Vector3f((float) Math.cos(Math.toRadians(yaw)),
(float) Math.sin(Math.toRadians(pitch)),
(float) Math.sin(Math.toRadians(yaw))));
front = getCameraDirection().normalize();
lookAt.identity().lookAt(position, new Vector3f(position).add(front), up);
}
public Vector3f getPosition() {
return position;
}
public float getPitch() {
return pitch;
}
public float getYaw() {
return yaw;
}
public float getRoll() {
return roll;
}
public Vector3f getCameraDirection() {
return cameraDirection;
}
public void setCameraDirection(Vector3f cameraDirection) {
this.cameraDirection = cameraDirection;
}
public Matrix4f getLookAtMatrix() {
return lookAt;
}
}
因此,如果您已经看到 lookAt
矩阵是使用 JOML 由三个向量定义的:
一个Camera Position
;
A Center (Front Vector + Camera Position)
;
和一个 Up
向量;
Camera Position
简单地定义了相机在世界中的位置(显然它会如何存在)。
Center
有点难以理解,但它所做的一切都定义了 相机应该注视的点 。
如果你的中心不变,那么你就得到了所谓的
Third Person Camera
,其中相机以可变半径旋转大约一个center
点。但是如果你想要
First Person Camera
,你的center
不能保持不变,因此必须每帧重新计算能够创造出流畅的第一人称效果。 如果见上文,在update()
方法中,front
向量使用cameraDirection
(相机面对的方向)在每一帧重新计算。cameraDirection
本身必须计算每一帧才能使上述过程正常工作。俯仰、偏航、滚转: What are they
它们只是围绕三个轴的旋转 (x = Pitch, y = Yaw, z = Roll)
(见图)。使用这些和一些三角函数,我们可以计算出相机的朝向 (cameraDirection
):
public void update() {
input();
setCameraDirection(new Vector3f((float) Math.cos(Math.toRadians(yaw)),
(float) Math.sin(Math.toRadians(pitch)),
(float) Math.sin(Math.toRadians(yaw))));
front = getCameraDirection().normalize();
lookAt.identity().lookAt(position, new Vector3f(position).add(front), up);
}
We can calculate x and z components of the direction vector, using the y rotation (YAW) and some trigonometry
We also need the y direction, which can be calculate through the (PITCH)
确保将方向向量(与任何方向向量一样)归一化为单位向量,否则中心将被弄乱
所以现在我们有一个 direction / front
和 position
向量。
UP
向量:
要像 view space
或 world space
或 tangent space
一样定义 "space"
,您需要三个向量来定义 space 的三个轴 - -> x轴、y轴和z轴
我对此了解不深(我可能是错的):
UP
向量定义视图space的y轴(x轴是方向向量,z轴(右向量)是x轴和y轴的叉积)。
UP
向量只是 (0, 1, 0)
并且永远不会改变,因为 1
表示它是直线向上(正 y 方向)。
还有 lookAt
矩阵函数:
//camPos //center //up
lookAt = new Matrix4f().lookAt(position, position.add(front), up);
制作人员:https://learnopengl.com/Getting-started/Camera、
绝对推荐查看本教程以获得深入的教程。