OpenGL开发(一)利用lwjgl类库绘制一个三角形
在绘制三角形之前,需要创建一个OpenGL上下文(Context)和一个用于显示的窗口。然而,这些操作在每个系统上都是不一样的,OpenGL将这部分抽离了出去。
GLFW
GLFW是一个专门针对OpenGL的C语言库,它提供了一些渲染物体所需的最低限度的接口。它允许用户创建OpenGL上下文,定义窗口参数以及处理用户输入。
一、只绘制渲染窗口的代码如下
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.system.MemoryUtil.NULL;
public class Demo01_open_window {
public static void main(String[] args){
glfwInit();//初始化
glfwWindowHint(GLFW_VISIBLE, GL_FALSE);//设置窗口的可见性为false
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);//设置窗口是否可以重新调整大小
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//OpenGL的主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);//OpenGL的副版本号
int width = 500;//窗口宽度
int height = 300;//窗口高度
long window = glfwCreateWindow(width, height, "Hello World!", NULL, NULL);//创建一个窗口,返回值是个long值。
glfwMakeContextCurrent(window);//通知GLFW将window的上下文设置为当前线程的主上下文
glfwShowWindow(window);//展示当前的窗口
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());//获取当前设备的一些属性
glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);//设置窗口的位置在最中间
GL.createCapabilities();//创建opengl上下文
//不间断的一直渲染窗口,如果没有这步循环,窗口会一闪而过。
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除之前渲染的缓存
glfwSwapBuffers(window); // 交换前后缓冲区
glfwPollEvents();//检查是否有键盘或是鼠标事件
}
glfwTerminate();//释放之前分配的所有资源。
}
}
运行结果:
上面代码中的相关说明
- 如果不在一开始就调用
glfwInit();
函数,则后面的函数将都会失效。 - OpenGL的主版本号在绘制图形时,一定要设置。
-
双缓冲(Double Buffer)
:应用程序使用单缓冲绘图时可能会存在图像闪烁的问题。 这是因为生成的图像不是一下子被绘制出来的,而是按照从左到右,由上而下逐像素地绘制而成的。最终图像不是在瞬间显示给用户,而是通过一步一步生成的,这会导致渲染的结果很不真实。为了规避这些问题,我们应用双缓冲渲染窗口应用程序。前缓冲保存着最终输出的图像,它会在屏幕上显示;而所有的的渲染指令都会在后缓冲上绘制。当所有的渲染指令执行完毕后,我们glfwSwapBuffers()
函数就是用来交换(Swap)前缓冲和后缓冲的,这样图像就立即呈显出来,之前提到的不真实感就消除了。
在窗口上绘制三角形
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.system.MemoryUtil;
import java.nio.FloatBuffer;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL20.glUseProgram;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import static org.lwjgl.system.MemoryUtil.NULL;
public class Demo02_draw_triangle {
public static void main(String[] args) throws Exception {
glfwInit();
glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//OpenGL的主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);//OpenGL的副版本号
int width = 600;
int height = 400;
long window = glfwCreateWindow(width, height, "Hello World!", NULL, NULL);
glfwMakeContextCurrent(window);
glfwShowWindow(window);
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);
GL.createCapabilities();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
//一下的代码是绘制三角形的代码
int programId = 0;//声明一个着色器程序ID
try {
//创建着色器程序
programId = glCreateProgram();
//创建并初始化顶点着色器
int vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderId, Utils.loadResource("/shader/demo02_vertex.vs"));
glCompileShader(vertexShaderId);
glAttachShader(programId, vertexShaderId);
//创建并初始化片元着色器
int fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderId, Utils.loadResource("/shader/demo02_fragment.fs"));
glCompileShader(fragmentShaderId);
glAttachShader(programId, fragmentShaderId);
//链接着色器,也可以理解成**。
glLinkProgram(programId);
//glValidateProgram(programId);
} catch (Exception e) {
e.printStackTrace();
}
int vaoId;
int vboId;
float[] vertices = new float[]{
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
FloatBuffer verticesBuffer = null;
try {
verticesBuffer = MemoryUtil.memAllocFloat(vertices.length);
verticesBuffer.put(vertices).flip();
// Create the VAO and bind to it
vaoId = glGenVertexArrays();
glBindVertexArray(vaoId);
// Create the VBO and bint to it
vboId = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
// Define structure of the data
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
// Unbind the VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Unbind the VAO
glBindVertexArray(0);
} finally {
if (verticesBuffer != null) {
MemoryUtil.memFree(verticesBuffer);
}
}
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//调用着色器程序
glUseProgram(programId);
// Bind to the VAO
glBindVertexArray(vaoId);
glEnableVertexAttribArray(0);
// Draw the vertices
glDrawArrays(GL_TRIANGLES, 0, 3);
// Restore state
glDisableVertexAttribArray(0);
glBindVertexArray(0);
glUseProgram(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
}
}
顶点着色器程序
#version 330
layout (location =0) in vec3 position;
void main()
{
gl_Position = vec4(position, 1.0);
}
片段着色器程序
#version 330
out vec4 fragColor;
void main()
{
fragColor = vec4(0.0, 0.5, 0.5, 1.0);
}
运行结果: