您的位置:首页 > 运维架构

【LWJGL2 WIKI】【现代OpenGL篇】用投影、视图、模型矩阵画方形

2016-01-19 23:42 513 查看
原文:http://wiki.lwjgl.org/wiki/The_Quad_with_Projection,_View_and_Model_matrices

Introduction 介绍

在OpenGL里,位移、旋转、缩放摄像机和模型用的矩阵必须我们自己计算。传统的OpenGL里有一堆帮忙计算矩阵的函数,但是OpenGL作为一个图形类库职责是画东西而不是算东西,所以现在这些工作最好是由我们自己来完成。本教程我们将用shader根据预先定义好的变换矩阵来计算每个顶点的位置。

So… what’s a matrix? 矩阵是什么

如果你想问这个问题,那你最好先学学数学。本教程不解释矩阵是什么,只教你怎样解决移动物体的问题。关于矩阵,你可以看看这个:[1]

3 Matrices 三个矩阵

为了移动模型和操作摄像机,我们要根据计算过程分为三个矩阵:

投影矩阵(Projection matrix)

视图矩阵(View matrix)

模型矩阵(Model matrix)

Projection matrix 投影矩阵

投影矩阵用来定义摄像机在3D世界中的可视区域,它也被称为截头锥体,你可以想象成它是一个顶被削平的金字塔,而你将要站在顶部向底部看去。因此通常宽高和我们要绘制的矩阵(即视口,viewport)有相同的横纵比。我们可以定义金字塔的深度,所有塔内的部分都会被画在屏幕上,塔外部分则削掉以节省OpenGL性能开销。金字塔底是塔顶的缩放版(仍然应有相同的横纵比),缩放程度由我们的“视野”决定。

把投影矩阵想象成摄像机镜头的数学抽象,用此矩阵我们可以定义许多东西,比如最终图象的横纵比、放大系数、可视矩离。用图表示大概是:



还记得OpenGL默认的每个轴的坐标范围是[-1,1]吗,使用投影矩阵可以改变这个范围。比如,当我们使用16:10的宽屏分辨率时,我们的高度范围仍然是[-1,1],但是宽度范围扩充为[-1.6,1.6]更合理。如果我们在矩阵里没用正确的横纵比,最终图片将看起来很变形。

矩阵应该是这样的:



代码:

// Setup projection matrix
projectionMatrix = new Matrix4f();
float fieldOfView = 60f;
float aspectRatio = (float)WIDTH / (float)HEIGHT;
float near_plane = 0.1f;
float far_plane = 100f;

float y_scale = this.coTangent(this.degreesToRadians(fieldOfView / 2f));
float x_scale = y_scale / aspectRatio;
float frustum_length = far_plane - near_plane;

projectionMatrix.m00 = x_scale;
projectionMatrix.m11 = y_scale;
projectionMatrix.m22 = -((far_plane + near_plane) / frustum_length);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * near_plane * far_plane) / frustum_length);
projectionMatrix.m33 = 0;


两个额外的函数cotangent和degressToRadians是简单的帮助函数,cotangent其实就相当于1/Math.tan(角度),因为视野角是角度格式,必须转换为弧度才可用(Math.tan方法参数要求是弧度),把角度转换为弧度只需要乘以PI/180。

View matrix 视图矩阵

投影矩阵是摄像机的镜头,视图矩阵则是摄像机本身,我们可以随意旋转和移动摄像机,用视图矩阵乘以投影矩阵才会得到一个完整的摄像机的数学定义。

Model matrix 模型矩阵

对每个模型来说,最终的矩阵应该各不相同,这就是模型矩阵。就是说我们可以像旋转、移动、缩放摄像机一样同样对待模型。

Manipulating matrices 操作矩阵

LWJGL提供了许多可以操作矩阵的静态方法,本例中我们使用一个4x4的浮点数矩阵,应该用到类Matrix4f。使用此类可以实现旋转、缩放和平移:

// Translate camera
Matrix4f.translate(cameraPos, viewMatrix, viewMatrix);


以上代码中,我们按某一向量平移视图矩阵(cameraPos是Vector3类型的变量),结果矩阵将覆盖我们当前矩阵(译注:因为两个参数里传的都viewMatrix,应该是一个是源矩阵,一个是结果矩阵),旋转和缩放的用法也是类似。

Moving, rotating and scaling our object 平移、旋转、缩放物体

操作矩阵需要Vector3对象,我们为每种操作单独定义一个Vector3变量:

private Vector3f modelPos = null;
private Vector3f modelAngle = null;
private Vector3f modelScale = null;


当定义方形时,我们也给这些向量一个标准初始值:

// Set the default quad rotation, scale and position values
modelPos = new Vector3f(0, 0, 0);
modelAngle = new Vector3f(0, 0, 0);
modelScale = new Vector3f(1, 1, 1);
cameraPos = new Vector3f(0, 0, -1);


在我们的程序循环逻辑部分,要创建完整的模型矩阵,这些变换必须按顺序进行:缩放、平移、旋转

// Scale, translate and rotate model
Matrix4f.scale(modelScale, modelMatrix, modelMatrix);
Matrix4f.translate(modelPos, modelMatrix, modelMatrix);
Matrix4f.rotate(this.degreesToRadians(modelAngle.z), new Vector3f(0, 0, 1),
modelMatrix, modelMatrix);
Matrix4f.rotate(this.degreesToRadians(modelAngle.y), new Vector3f(0, 1, 0),
modelMatrix, modelMatrix);
Matrix4f.rotate(this.degreesToRadians(modelAngle.x), new Vector3f(1, 0, 0),
modelMatrix, modelMatrix);


Uniform shader variables shader中的uniform变量

你得根据三个矩阵计算每个顶点的正确位置,因此我们的shader必须事先就得到这些信息,用uniform变量来实现。uniform变量是一种会在渲染管线里保持不变的变量,这正是我们所需要的,渲染前我们得上传新的矩阵数据。按下面这样定义uniform变量:

#version 150 core

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

in vec4 in_Position;


在main方法里,我们要算新的顶点坐标,in_Position是原来的顶点坐标:

gl_Position = projectionMatrix * viewMatrix * modelMatrix * in_Position;


片段shader不需要知道发生了什么。in变量与属性列表相连,uniform变量并不需要。我们把数据放进这些变量中,因此我们需要知道这些变量存在于我们shader(内存)的何处。可以这样获取:

// Get matrices uniform locations
projectionMatrixLocation = GL20.glGetUniformLocation(pId, "projectionMatrix");
viewMatrixLocation = GL20.glGetUniformLocation(pId, "viewMatrix");
modelMatrixLocation = GL20.glGetUniformLocation(pId, "modelMatrix");


得到的这些location是整数,一旦shader创建好,它们将不变。所以它们是全局的,当我们上传数据时正需要它们。

Uploading data to uniform variables 上传数据到uniform变量

我们已经有了投影、视图、模型矩阵,最后只需要把它们上传到uniform变量中去,在此之前有些事要做:

储存Matrix4f数据在一个FloatBuffer中,此FloatBuffer必须保存16个值。

Flip这个FloatBuffer

把FloatBuffer发送到uniform location

保存矩阵很容易,定义一个全局FloatBuffer保存16个值,我们的矩阵有一个叫store的函数,接收一个FloatBuffer然后把矩阵信息存在其中。Flip一个FloatBuffer之前也常做,应该驾轻就熟了。uniform location就是之前提到的当创建shader时就有的数据值。我们可以用glUniformMatrix4上传FloatBuffer,保存、flip、上传三个矩阵,别忘了还要绑定shader:

// Upload matrices to the uniform variables
GL20.glUseProgram(pId);

projectionMatrix.store(matrix44Buffer); matrix44Buffer.flip();
GL20.glUniformMatrix4(projectionMatrixLocation, false, matrix44Buffer);
viewMatrix.store(matrix44Buffer); matrix44Buffer.flip();
GL20.glUniformMatrix4(viewMatrixLocation, false, matrix44Buffer);
modelMatrix.store(matrix44Buffer); matrix44Buffer.flip();
GL20.glUniformMatrix4(modelMatrixLocation, false, matrix44Buffer);

GL20.glUseProgram(0);


The result 结果



现在即使视口不是正方形,方形仍然被无拉伸地正确渲染。这是理所当然的,因为我们用了投影矩阵,它调整数值以适应我们使用的横纵比。

Complete source code 完整代码

Vertex shader

#version 150 core

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

in vec4 in_Position;
in vec4 in_Color;
in vec2 in_TextureCoord;

out vec4 pass_Color;
out vec2 pass_TextureCoord;

void main(void) {
gl_Position = in_Position;
// Override gl_Position with our new calculated position
gl_Position = projectionMatrix * viewMatrix * modelMatrix * in_Position;

pass_Color = in_Color;
pass_TextureCoord = in_TextureCoord;
}


Fragment shader

#version 150 core

uniform sampler2D texture_diffuse;

in vec4 pass_Color;
in vec2 pass_TextureCoord;

out vec4 out_Color;

void main(void) {
out_Color = pass_Color;
// Override out_Color with our texture pixel
out_Color = texture2D(texture_diffuse, pass_TextureCoord);
}


VertexData class

public class VertexData {
// Vertex data
private float[] xyzw = new float[] {0f, 0f, 0f, 1f};
private float[] rgba = new float[] {1f, 1f, 1f, 1f};
private float[] st = new float[] {0f, 0f};

// The amount of bytes an element has
public static final int elementBytes = 4;

// Elements per parameter
public static final int positionElementCount = 4;
public static final int colorElementCount = 4;
public static final int textureElementCount = 2;

// Bytes per parameter
public static final int positionBytesCount = positionElementCount * elementBytes;
public static final int colorByteCount = colorElementCount * elementBytes;
public static final int textureByteCount = textureElementCount * elementBytes;

// Byte offsets per parameter
public static final int positionByteOffset = 0;
public static final int colorByteOffset = positionByteOffset + positionBytesCount;
public static final int textureByteOffset = colorByteOffset + colorByteCount;

// The amount of elements that a vertex has
public static final int elementCount = positionElementCount +
colorElementCount + textureElementCount;
// The size of a vertex in bytes, like in C/C++: sizeof(Vertex)
public static final int stride = positionBytesCount + colorByteCount +
textureByteCount;

// Setters
public void setXYZ(float x, float y, float z) {
this.setXYZW(x, y, z, 1f);
}

public void setRGB(float r, float g, float b) {
this.setRGBA(r, g, b, 1f);
}

public void setST(float s, float t) {
this.st = new float[] {s, t};
}

public void setXYZW(float x, float y, float z, float w) {
this.xyzw = new float[] {x, y, z, w};
}

public void setRGBA(float r, float g, float b, float a) {
this.rgba = new float[] {r, g, b, 1f};
}

// Getters
public float[] getElements() {
float[] out = new float[VertexData.elementCount];
int i = 0;

// Insert XYZW elements
out[i++] = this.xyzw[0];
out[i++] = this.xyzw[1];
out[i++] = this.xyzw[2];
out[i++] = this.xyzw[3];
// Insert RGBA elements
out[i++] = this.rgba[0];
out[i++] = this.rgba[1];
out[i++] = this.rgba[2];
out[i++] = this.rgba[3];
// Insert ST elements
out[i++] = this.st[0];
out[i++] = this.st[1];

return out;
}

public float[] getXYZW() {
return new float[] {this.xyzw[0], this.xyzw[1], this.xyzw[2], this.xyzw[3]};
}

public float[] getXYZ() {
return new float[] {this.xyzw[0], this.xyzw[1], this.xyzw[2]};
}

public float[] getRGBA() {
return new float[] {this.rgba[0], this.rgba[1], this.rgba[2], this.rgba[3]};
}

public float[] getRGB() {
return new float[] {this.rgba[0], this.rgba[1], this.rgba[2]};
}

public float[] getST() {
return new float[] {this.st[0], this.st[1]};
}
}


Application

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

import de.matthiasmann.twl.utils.PNGDecoder;
import de.matthiasmann.twl.utils.PNGDecoder.Format;

public class TheQuadExampleMoving {
// Entry point for the application
public static void main(String[] args) {
new TheQuadExampleMoving();
}

// Setup variables
private final String WINDOW_TITLE = "The Quad: Moving";
private final int WIDTH = 320;
private final int HEIGHT = 200;
private final double PI = 3.14159265358979323846;
// Quad variables
private int vaoId = 0;
private int vboId = 0;
private int vboiId = 0;
private int indicesCount = 0;
private VertexData[] vertices = null;
private ByteBuffer verticesByteBuffer = null;
// Shader variables
private int pId = 0;
// Texture variables
private int[] texIds = new int[] {0, 0};
private int textureSelector = 0;
// Moving variables
private int projectionMatrixLocation = 0;
private int viewMatrixLocation = 0;
private int modelMatrixLocation = 0;
private Matrix4f projectionMatrix = null;
private Matrix4f viewMatrix = null;
private Matrix4f modelMatrix = null;
private Vector3f modelPos = null; private Vector3f modelAngle = null; private Vector3f modelScale = null;
private Vector3f cameraPos = null;
private FloatBuffer matrix44Buffer = null;

public TheQuadExampleMoving() {
// Initialize OpenGL (Display)
this.setupOpenGL();

this.setupQuad();
this.setupShaders();
this.setupTextures();
this.setupMatrices();

while (!Display.isCloseRequested()) {
// Do a single loop (logic/render)
this.loopCycle();

// Force a maximum FPS of about 60
Display.sync(60);
// Let the CPU synchronize with the GPU if GPU is tagging behind
Display.update();
}

// Destroy OpenGL (Display)
this.destroyOpenGL();
}

private void setupMatrices() {
// Setup projection matrix projectionMatrix = new Matrix4f(); float fieldOfView = 60f; float aspectRatio = (float)WIDTH / (float)HEIGHT; float near_plane = 0.1f; float far_plane = 100f; float y_scale = this.coTangent(this.degreesToRadians(fieldOfView / 2f)); float x_scale = y_scale / aspectRatio; float frustum_length = far_plane - near_plane; projectionMatrix.m00 = x_scale; projectionMatrix.m11 = y_scale; projectionMatrix.m22 = -((far_plane + near_plane) / frustum_length); projectionMatrix.m23 = -1; projectionMatrix.m32 = -((2 * near_plane * far_plane) / frustum_length); projectionMatrix.m33 = 0;

// Setup view matrix
viewMatrix = new Matrix4f();

// Setup model matrix
modelMatrix = new Matrix4f();

// Create a FloatBuffer with the proper size to store our matrices later
matrix44Buffer = BufferUtils.createFloatBuffer(16);
}

private void setupTextures() {
texIds[0] = this.loadPNGTexture("assets/images/stGrid1.png", GL13.GL_TEXTURE0);
texIds[1] = this.loadPNGTexture("assets/images/stGrid2.png", GL13.GL_TEXTURE0);

this.exitOnGLError("setupTexture");
}

private void setupOpenGL() {
// Setup an OpenGL context with API version 3.2
try {
PixelFormat pixelFormat = new PixelFormat();
ContextAttribs contextAtrributes = new ContextAttribs(3, 2)
.withForwardCompatible(true)
.withProfileCore(true);

Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.setTitle(WINDOW_TITLE);
Display.create(pixelFormat, contextAtrributes);

GL11.glViewport(0, 0, WIDTH, HEIGHT);
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(-1);
}

// Setup an XNA like background color
GL11.glClearColor(0.4f, 0.6f, 0.9f, 0f);

// Map the internal OpenGL coordinate system to the entire screen
GL11.glViewport(0, 0, WIDTH, HEIGHT);

this.exitOnGLError("setupOpenGL");
}

private void setupQuad() {
// We'll define our quad using 4 vertices of the custom 'TexturedVertex' class
VertexData v0 = new VertexData();
v0.setXYZ(-0.5f, 0.5f, 0); v0.setRGB(1, 0, 0); v0.setST(0, 0);
VertexData v1 = new VertexData();
v1.setXYZ(-0.5f, -0.5f, 0); v1.setRGB(0, 1, 0); v1.setST(0, 1);
VertexData v2 = new VertexData();
v2.setXYZ(0.5f, -0.5f, 0); v2.setRGB(0, 0, 1); v2.setST(1, 1);
VertexData v3 = new VertexData();
v3.setXYZ(0.5f, 0.5f, 0); v3.setRGB(1, 1, 1); v3.setST(1, 0);

vertices = new VertexData[] {v0, v1, v2, v3};

// Put each 'Vertex' in one FloatBuffer
verticesByteBuffer = BufferUtils.createByteBuffer(vertices.length *
VertexData.stride);
FloatBuffer verticesFloatBuffer = verticesByteBuffer.asFloatBuffer();
for (int i = 0; i < vertices.length; i++) {
// Add position, color and texture floats to the buffer
verticesFloatBuffer.put(vertices[i].getElements());
}
verticesFloatBuffer.flip();

// OpenGL expects to draw vertices in counter clockwise order by default
byte[] indices = {
0, 1, 2,
2, 3, 0
};
indicesCount = indices.length;
ByteBuffer indicesBuffer = BufferUtils.createByteBuffer(indicesCount);
indicesBuffer.put(indices);
indicesBuffer.flip();

// Create a new Vertex Array Object in memory and select it (bind)
vaoId = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoId);

// Create a new Vertex Buffer Object in memory and select it (bind)
vboId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesFloatBuffer, GL15.GL_STREAM_DRAW);

// Put the position coordinates in attribute list 0
GL20.glVertexAttribPointer(0, VertexData.positionElementCount, GL11.GL_FLOAT,
false, VertexData.stride, VertexData.positionByteOffset);
// Put the color components in attribute list 1
GL20.glVertexAttribPointer(1, VertexData.colorElementCount, GL11.GL_FLOAT,
false, VertexData.stride, VertexData.colorByteOffset);
// Put the texture coordinates in attribute list 2
GL20.glVertexAttribPointer(2, VertexData.textureElementCount, GL11.GL_FLOAT,
false, VertexData.stride, VertexData.textureByteOffset);

GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

// Deselect (bind to 0) the VAO
GL30.glBindVertexArray(0);

// Create a new VBO for the indices and select it (bind) - INDICES
vboiId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer,
GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);

// Set the default quad rotation, scale and position values modelPos = new Vector3f(0, 0, 0); modelAngle = new Vector3f(0, 0, 0); modelScale = new Vector3f(1, 1, 1); cameraPos = new Vector3f(0, 0, -1);

this.exitOnGLError("setupQuad");
}

private void setupShaders() {
// Load the vertex shader
int vsId = this.loadShader("assets/shaders/moving/vertex.glsl", GL20.GL_VERTEX_SHADER);
// Load the fragment shader
int fsId = this.loadShader("assets/shaders/moving/fragment.glsl", GL20.GL_FRAGMENT_SHADER);

// Create a new shader program that links both shaders
pId = GL20.glCreateProgram();
GL20.glAttachShader(pId, vsId);
GL20.glAttachShader(pId, fsId);

// Position information will be attribute 0
GL20.glBindAttribLocation(pId, 0, "in_Position");
// Color information will be attribute 1
GL20.glBindAttribLocation(pId, 1, "in_Color");
// Textute information will be attribute 2
GL20.glBindAttribLocation(pId, 2, "in_TextureCoord");

GL20.glLinkProgram(pId);
GL20.glValidateProgram(pId);

// Get matrices uniform locations
projectionMatrixLocation = GL20.glGetUniformLocation(pId,"projectionMatrix");
viewMatrixLocation = GL20.glGetUniformLocation(pId, "viewMatrix");
modelMatrixLocation = GL20.glGetUniformLocation(pId, "modelMatrix");

this.exitOnGLError("setupShaders");
}

private void logicCycle() {
//-- Input processing
float rotationDelta = 15f;
float scaleDelta = 0.1f;
float posDelta = 0.1f;
Vector3f scaleAddResolution = new Vector3f(scaleDelta, scaleDelta, scaleDelta);
Vector3f scaleMinusResolution = new Vector3f(-scaleDelta, -scaleDelta,
-scaleDelta);

while(Keyboard.next()) {
// Only listen to events where the key was pressed (down event)
if (!Keyboard.getEventKeyState()) continue;

// Switch textures depending on the key released
switch (Keyboard.getEventKey()) {
case Keyboard.KEY_1:
textureSelector = 0;
break;
case Keyboard.KEY_2:
textureSelector = 1;
break;
}

// Change model scale, rotation and translation values
switch (Keyboard.getEventKey()) {
// Move
case Keyboard.KEY_UP:
modelPos.y += posDelta;
break;
case Keyboard.KEY_DOWN:
modelPos.y -= posDelta;
break;
// Scale
case Keyboard.KEY_P:
Vector3f.add(modelScale, scaleAddResolution, modelScale);
break;
case Keyboard.KEY_M:
Vector3f.add(modelScale, scaleMinusResolution, modelScale);
break;
// Rotation
case Keyboard.KEY_LEFT:
modelAngle.z += rotationDelta;
break;
case Keyboard.KEY_RIGHT:
modelAngle.z -= rotationDelta;
break;
}
}

//-- Update matrices
// Reset view and model matrices
viewMatrix = new Matrix4f();
modelMatrix = new Matrix4f();

// Translate camera Matrix4f.translate(cameraPos, viewMatrix, viewMatrix);

// Scale, translate and rotate model Matrix4f.scale(modelScale, modelMatrix, modelMatrix); Matrix4f.translate(modelPos, modelMatrix, modelMatrix); Matrix4f.rotate(this.degreesToRadians(modelAngle.z), new Vector3f(0, 0, 1), modelMatrix, modelMatrix); Matrix4f.rotate(this.degreesToRadians(modelAngle.y), new Vector3f(0, 1, 0), modelMatrix, modelMatrix); Matrix4f.rotate(this.degreesToRadians(modelAngle.x), new Vector3f(1, 0, 0), modelMatrix, modelMatrix);

// Upload matrices to the uniform variables GL20.glUseProgram(pId); projectionMatrix.store(matrix44Buffer); matrix44Buffer.flip(); GL20.glUniformMatrix4(projectionMatrixLocation, false, matrix44Buffer); viewMatrix.store(matrix44Buffer); matrix44Buffer.flip(); GL20.glUniformMatrix4(viewMatrixLocation, false, matrix44Buffer); modelMatrix.store(matrix44Buffer); matrix44Buffer.flip(); GL20.glUniformMatrix4(modelMatrixLocation, false, matrix44Buffer); GL20.glUseProgram(0);

this.exitOnGLError("logicCycle");
}

private void renderCycle() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

GL20.glUseProgram(pId);

// Bind the texture
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texIds[textureSelector]);

// Bind to the VAO that has all the information about the vertices
GL30.glBindVertexArray(vaoId);
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
GL20.glEnableVertexAttribArray(2);

// Bind to the index VBO that has all the information about the order of the vertices
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);

// Draw the vertices
GL11.glDrawElements(GL11.GL_TRIANGLES, indicesCount, GL11.GL_UNSIGNED_BYTE, 0);

// Put everything back to default (deselect)
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);
GL30.glBindVertexArray(0);

GL20.glUseProgram(0);

this.exitOnGLError("renderCycle");
}

private void loopCycle() {
// Update logic
this.logicCycle();
// Update rendered frame
this.renderCycle();

this.exitOnGLError("loopCycle");
}

private void destroyOpenGL() {
// Delete the texture
GL11.glDeleteTextures(texIds[0]);
GL11.glDeleteTextures(texIds[1]);

// Delete the shaders
GL20.glUseProgram(0);
GL20.glDeleteProgram(pId);

// Select the VAO
GL30.glBindVertexArray(vaoId);

// Disable the VBO index from the VAO attributes list
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);

// Delete the vertex VBO
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(vboId);

// Delete the index VBO
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(vboiId);

// Delete the VAO
GL30.glBindVertexArray(0);
GL30.glDeleteVertexArrays(vaoId);

this.exitOnGLError("destroyOpenGL");

Display.destroy();
}

private int loadShader(String filename, int type) {
StringBuilder shaderSource = new StringBuilder();
int shaderID = 0;

try {
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line;
while ((line = reader.readLine()) != null) {
shaderSource.append(line).append("\n");
}
reader.close();
} catch (IOException e) {
System.err.println("Could not read file.");
e.printStackTrace();
System.exit(-1);
}

shaderID = GL20.glCreateShader(type);
GL20.glShaderSource(shaderID, shaderSource);
GL20.glCompileShader(shaderID);

if (GL20.glGetShader(shaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
System.err.println("Could not compile shader.");
System.exit(-1);
}

this.exitOnGLError("loadShader");

return shaderID;
}

private int loadPNGTexture(String filename, int textureUnit) {
ByteBuffer buf = null;
int tWidth = 0;
int tHeight = 0;

try {
// Open the PNG file as an InputStream
InputStream in = new FileInputStream(filename);
// Link the PNG decoder to this stream
PNGDecoder decoder = new PNGDecoder(in);

// Get the width and height of the texture
tWidth = decoder.getWidth();
tHeight = decoder.getHeight();

// Decode the PNG file in a ByteBuffer
buf = ByteBuffer.allocateDirect(
4 * decoder.getWidth() * decoder.getHeight());
decoder.decode(buf, decoder.getWidth() * 4, Format.RGBA);
buf.flip();

in.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}

// Create a new texture object in memory and bind it
int texId = GL11.glGenTextures();
GL13.glActiveTexture(textureUnit);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texId);

// All RGB bytes are aligned to each other and each component is 1 byte
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);

// Upload the texture data and generate mip maps (for scaling)
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, tWidth, tHeight, 0,
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buf);
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);

// Setup the ST coordinate system
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);

// Setup what to do when the texture has to be scaled
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_LINEAR_MIPMAP_LINEAR);

this.exitOnGLError("loadPNGTexture");

return texId;
}

private float coTangent(float angle) {
return (float)(1f / Math.tan(angle));
}

private float degreesToRadians(float degrees) {
return degrees * (float)(PI / 180d);
}

private void exitOnGLError(String errorMessage) {
int errorValue = GL11.glGetError();

if (errorValue != GL11.GL_NO_ERROR) {
String errorString = GLU.gluErrorString(errorValue);
System.err.println("ERROR - " + errorMessage + ": " + errorString);

if (Display.isCreated()) Display.destroy();
System.exit(-1);
}
}
}


Credit

Mathias Verboven (moci)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java lwjgl