graphics.Shader:
package com.blank.Flappy.graphics;
import com.blank.Flappy.maths.Matrix4f;
import com.blank.Flappy.maths.Vector3f;
import com.blank.Flappy.utils.ShaderUtils;
import java.util.Map;
import java.util.HashMap;
import static org.lwjgl.opengl.GL20.*;
public class Shader {
public static final int VERTEX_ATTRIB = 0;
public static final int TCOORD_ATTRIB = 1;
public static Shader BG, BIRD, PIPE;
private final int ID;
private Map<String, Integer> locationCache = new HashMap<String, Integer>(); //缓存路径 不会实时更新
private boolean enabled = false; //创建一个布尔变量用来检测着色器的启用和禁用
public Shader(String vertex, String fragment){ //加载顶点和片元着色器
ID = ShaderUtils.load(vertex, fragment);
}
public static void loadAll(){ //读取着色器文件
BG = new Shader("shaders/bg.vert","shaders/bg.frag");
}
public int getUniform(String name){ //创建方法用于获取uniform变量
if (locationCache.containsKey(name)){
return locationCache.get(name);
}
int result = glGetUniformLocation(ID, name);
if (result == -1)
System.err.println("Could not find uniform variable '" + name + "'!");
else
locationCache.put(name, result);
return result;
}
//设置不同类型的uniform变量(用于顶点和片元着色器中表示变换矩阵,材质,光照参数和颜色等信息。
public void setUniform1i(String name, int value){
if(!enabled) enable();
glUniform1i(getUniform(name), value);
}
public void setUniform1f(String name, float value){
if(!enabled) enable();
glUniform1f(getUniform(name), value);
}
public void setUniform2f(String name, float x, float y){
if(!enabled) enable();
glUniform2f(getUniform(name), x, y);
}
public void setUniform3f(String name, Vector3f vector){
if(!enabled) enable();
glUniform3f(getUniform(name), vector.x, vector.y, vector.z);
}
public void setUniformMat4f(String name, Matrix4f matrix){
if(!enabled) enable();
glUniformMatrix4fv(getUniform(name), false, matrix.toFloatBuffer()); //因为OpenGL需要列优先读取,所以矩阵不需要转置
}
public void enable(){ //启用着色器
glUseProgram(ID);
enabled = true;
}
public void disable(){ //禁用着色器 用完之后一定要禁用以避免系统资源的浪费
glUseProgram(0);
enabled = false;
}
}
graphics.Texture:
package com.blank.Flappy.graphics;
import com.blank.Flappy.utils.BufferUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.IOException;
import static org.lwjgl.opengl.GL11.*;
public class Texture {
private int width, height, texture;
public Texture(String path){
texture = load(path);
}
private int load(String path){ //从图片中读取材纹理信息
int[] pixels = null;
try{
BufferedImage image = ImageIO.read(new FileInputStream(path)); //读取图片文件
width = image.getWidth(); //获取图片宽
height = image.getHeight(); //获取图片高
pixels = new int[width * height]; //创建数组获取图片像素
image.getRGB(0, 0, width, height, pixels, 0, width); //获取图片颜色
}catch (IOException e){
e.printStackTrace();
}
int[] data = new int[width * height]; //创建数组存储图片颜色信息
for(int i = 0; i < width * height; i++){ //根据OpenGL的读取方式获取颜色信息
int a = (pixels[i] & 0xff000000) >> 24;
int r = (pixels[i] & 0xff0000) >> 16;
int g = (pixels[i] & 0xff00) >> 8;
int b = (pixels[i] & 0xff);
data[i] = a << 24 | b << 16 | g << 8 | r; //OpenGL的颜色读取顺序
}
int result = glGenTextures(); //创建int变量存储纹理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //设置纹理参数(二维纹理,缩小纹理,临近采样)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //(二维纹理,放大纹理,临近采样)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data)); //指定二维纹理图片 将之前获取的参数传入
return result;
}
public void bind(){
glBindTexture(GL_TEXTURE_2D, texture); //将二维纹理绑定至texture变量上
}
public void unbind(){
glBindTexture(GL_TEXTURE_2D, 0); //取消绑定
}
}
graphics.VertexArray:
package com.blank.Flappy.graphics;
import com.blank.Flappy.utils.BufferUtils;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL30.*;
public class VertexArray {
private int vao, vbo, ibo, tbo; //顶点数组对象,顶点缓冲对象,索引(index)缓冲对象,纹理缓冲对象
private int count; //需要渲染的顶点数量
public VertexArray(float[] vertices, byte[] indices, float[] textureCoordinates){
count = indices.length; //用索引数组长度表示顶点数量
vao = glGenVertexArrays(); //生成顶点数组
glBindVertexArray(vao); //绑定顶点数组
vbo = glGenBuffers(); //生成缓冲对象
glBindBuffer(GL_ARRAY_BUFFER, vbo); //绑定缓冲对象
glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW); //创建并初始化缓冲对象的数据
glVertexAttribPointer(Shader.VERTEX_ATTRIB, 3, GL_FLOAT, false, 0, 0); //定义通用顶点属性数据的数组 (size为3是因为vertex中有x,y,z三种参数)
glEnableVertexAttribArray(Shader.VERTEX_ATTRIB);
tbo = glGenBuffers(); //纹理对象
glBindBuffer(GL_ARRAY_BUFFER, tbo);
glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL_STATIC_DRAW);
glVertexAttribPointer(Shader.TCOORD_ATTRIB, 2, GL_FLOAT, false, 0, 0); //(size为2是因为texture中只有x,y两种参数)
glEnableVertexAttribArray(Shader.TCOORD_ATTRIB);
ibo = glGenBuffers(); //索引缓冲对象
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //解绑索引缓冲
glBindBuffer(GL_ARRAY_BUFFER, 0); //解绑数组缓冲
glBindVertexArray(0); //解绑顶点数组
}
public void bind(){
glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
}
public void unbind(){
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
public void draw(){
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0); //从数组数据三角化渲染图元
}
public void render(){
bind();
draw();
}
}
input.Input:
package com.blank.Flappy.input;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWKeyCallback;
public class Input extends GLFWKeyCallback { //反馈用户按键
public static boolean[] keys = new boolean[65536];
public void invoke(long window, int key, int scancode, int action, int mods) {
keys[key] = action != GLFW.GLFW_RELEASE;
}
}
level.Level:
package com.blank.Flappy.level;
import com.blank.Flappy.graphics.Shader;
import com.blank.Flappy.graphics.Texture;
import com.blank.Flappy.graphics.VertexArray;
public class Level {
private VertexArray background;
private Texture bgTexture;
public Level(){
float[] vertices = new float[]{ //与16:9的矩阵对应
-10.0f, -10.0f * 9.0f / 16.0f, 0.0f,
-10.0f, 10.0f * 9.0f / 16.0f, 0.0f,
0.0f , 10.0f * 9.0f / 16.0f, 0.0f,
0.0f , -10.0f * 9.0f / 16.0f, 0.0f
};
byte[] indices = new byte[]{ //重复使用相同的顶点来防止创建多余的顶点
0, 1, 2, //第一个三角形
2, 3, 0 //第二个三角形
};
float[] tcs = new float[]{
0, 1,
0, 0,
1, 0,
1, 1
};
background = new VertexArray(vertices, indices, tcs);
bgTexture = new Texture("res/bg.jpeg");
}
public void render(){
bgTexture.bind();
Shader.BG.enable();
background.render();
Shader.BG.disable();
bgTexture.unbind();
}
}
maths.Matrix4f:
package com.blank.Flappy.maths;
import java.nio.FloatBuffer;
import com.blank.Flappy.utils.BufferUtils;
public class Matrix4f { //定义矩阵类
public static final int SIZE = 4 * 4;
public float[] elements = new float[SIZE];
public Matrix4f(){
}
public static Matrix4f identity(){ //初始化矩阵
Matrix4f result = new Matrix4f();
for (int i = 0; i < SIZE; i++){
result.elements[i] = 0.0f;
}
result.elements[0 + 0 * 4] = 1.0f;
result.elements[1 + 1 * 4] = 1.0f;
result.elements[2 + 2 * 4] = 1.0f;
result.elements[3 + 3 * 4] = 1.0f;
return result;
}
public static Matrix4f orthographic(float left, float right, float bottom, float top, float near, float far){ //创建正交矩阵数组
Matrix4f result = identity();
result.elements[0 + 0 * 4] = 2.0f / (right - left);
result.elements[1 + 1 * 4] = 2.0f / (top - bottom);
result.elements[2 + 2 * 4] = 2.0f / (near - far);
result.elements[0 + 3 * 4] = (left + right) / (left - right);
result.elements[1 + 3 * 4] = (bottom + top) / (bottom - top);
result.elements[2 + 3 * 4] = (far + near) / (far - near);
return result;
}
public static Matrix4f translate(Vector3f vector){ //平移矩阵
Matrix4f result = identity();
result.elements[0 + 3 * 4] = vector.x;
result.elements[1 + 3 * 4] = vector.y;
result.elements[2 + 3 * 4] = vector.z;
return result;
}
public static Matrix4f rotate(float angle){ //旋转矩阵
Matrix4f result = identity();
float r = (float) Math.toRadians(angle);
float cos = (float) Math.cos(r);
float sin = (float) Math.sin(r);
result.elements[0 + 0 * 4] = cos;
result.elements[1 + 0 * 4] = sin;
result.elements[0 + 1 * 4] = -sin;
result.elements[1 + 1 * 4] = cos;
return result;
}
public Matrix4f multiply(Matrix4f matrix){ //矩阵乘法
Matrix4f result = new Matrix4f();
for(int y = 0; y < 4; y++){
for(int x = 0; x < 4; x++){
float sum = 0.0f;
for(int e = 0; e < 4; e++){
sum += this.elements[x + e * 4] * matrix.elements[e + y * 4];
}
result.elements[x + y *4] = sum;
}
}
return result;
}
public FloatBuffer toFloatBuffer(){ //返回缓存数据
return BufferUtils.createFloatBuffer(elements);
}
}
maths.Vector3f:
package com.blank.Flappy.maths;
public class Vector3f { //定义向量类并初始化
public float x, y, z;
public Vector3f(){
x = 0.0f;
y = 0.0f;
z = 0.0f;
}
public Vector3f(float x, float y, float z){
this.x = x;
this.y = y;
this.z = z;
}
}
utils.BufferUtils:
package com.blank.Flappy.utils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
public class BufferUtils { //创建三种缓存 用来存放不同的数据类型数组的信息
private BufferUtils(){
}
public static ByteBuffer createByteBuffer(byte[] array){
ByteBuffer result = ByteBuffer.allocateDirect(array.length).order(ByteOrder.nativeOrder());
result.put(array).flip();
return result;
}
public static FloatBuffer createFloatBuffer(float[] array){
FloatBuffer result = ByteBuffer.allocateDirect(array.length << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
result.put(array).flip();
return result;
}
public static IntBuffer createIntBuffer(int[] array){
IntBuffer result = ByteBuffer.allocateDirect(array.length << 2).order(ByteOrder.nativeOrder()).asIntBuffer();
result.put(array).flip();
return result;
}
}
utils.FileUtils:
package com.blank.Flappy.utils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileUtils {
private FileUtils(){
}
public static String loadAsString(String file){ //以数组形式读取文件 便于将图片转换为矩阵和向量
StringBuilder result = new StringBuilder();
try{
BufferedReader reader = new BufferedReader(new FileReader(file));
String buffer = "";
while((buffer = reader.readLine()) != null){
result.append(buffer + '\n');
}
reader.close();
}catch (IOException e){
e.printStackTrace();
}
return result.toString();
}
}
utils.ShaderUtils:
package com.blank.Flappy.utils;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
public class ShaderUtils {
private ShaderUtils(){
}
public static int load(String vertPath, String fragPath){ //读取顶点和片元着色器文件(由于是2D的 所以并没有用到几何着色器)
String vert = FileUtils.loadAsString(vertPath);
String frag = FileUtils.loadAsString(fragPath);
return create(vert,frag);
}
public static int create(String vert, String frag){
int program = glCreateProgram(); //创建一个program对象用于附加着色器对象
int vertID = glCreateShader(GL_VERTEX_SHADER); //创建着色器对象
int fragID = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertID, vert); //修改着色器对象中的源代码
glShaderSource(fragID, frag);
glCompileShader(vertID); //编译着色器
if (glGetShaderi(vertID,GL_COMPILE_STATUS) == GL_FALSE){
System.err.println("Failed to compile vertex shader!");
System.err.println(glGetShaderInfoLog(vertID));
return -1;
}
glCompileShader(fragID);
if (glGetShaderi(fragID,GL_COMPILE_STATUS) == GL_FALSE){
System.err.println("Failed to compile fragment shader!");
System.err.println(glGetShaderInfoLog(fragID));
return -1;
}
glAttachShader(program, vertID); //将着色器对象附着到program对象上
glAttachShader(program, fragID);
glLinkProgram(program); //链接program对象,将两种着色器对象用于创建可执行文件
glValidateProgram(program); //验证program对象已经链接
glDeleteShader(vertID); //删除已经附着好的着色器对象 释放内存
glDeleteShader(fragID);
return program;
}
}
Main:
package com.blank.Flappy;
import com.blank.Flappy.graphics.Shader;
import com.blank.Flappy.input.Input;
import com.blank.Flappy.level.Level;
import com.blank.Flappy.maths.Matrix4f;
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.GL13.*;
import static org.lwjgl.system.MemoryUtil.*;
public class Main implements Runnable{
private int width = 1280;
private int height = 720;
private Thread thread;
private boolean running = false;
private long window;
private Level level;
public void start(){
running = true;
thread = new Thread(this,"Game"); //创建名为Game的线程
thread.start();
}
private void init(){ //初始化
if(!glfwInit()){ //初始化失败
System.err.println("Could not initialize GLFW!");
return;
}
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); //配置上下文环境
window = glfwCreateWindow(width, height, "Flappy Blank", NULL, NULL); //创建窗口
if(window == NULL){ //创建窗口失败
System.err.println("Could not create GLFW window!");
return;
}
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); //获取默认显示屏
glfwSetWindowPos(window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2); //将窗口位置设置为默认显示屏中央
glfwSetKeyCallback(window, new Input()); //获取用户输入按键信息
glfwMakeContextCurrent(window); //设置上下文
glfwShowWindow(window); //显示窗口
GL.createCapabilities(); //初始化上下文
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); //设置背景颜色
glEnable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE1); //与setUniform1i中的value=1对应
System.out.println("OPENGL: " + glGetString(GL_VERSION)); //显示OpenGL版本
Shader.loadAll();
Matrix4f pr_matrix = Matrix4f.orthographic(-10.0f, 10.0f, -10.0f * 9.0f / 16.0f, 10.0f * 9.0f / 16.0f, -1.0f, 1.0f); //16:9的背景比例
Shader.BG.setUniformMat4f("pr_matrix", pr_matrix);
Shader.BG.setUniform1i("tex", 1);
level = new Level();
}
public void run(){
init();
while(running){
update();
render();
//检测窗口是否关闭
if(glfwWindowShouldClose(window)){
running = false;
}
}
}
private void update(){
glfwPollEvents();
}
private void render(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
level.render();
int error = glGetError(); //检查错误
if (error != GL_NO_ERROR){
System.out.println(error);
}
glfwSwapBuffers(window);
}
public static void main(String[] args){
new Main().start();
}
}
bg.frag:
#version 330 core
layout (location = 0) out vec4 color;
in DATA
{
vec2 tc;
} fs_in;
uniform sampler2D tex;
void main()
{
color = texture(tex, fs_in.tc);
}
bg.vert:
#version 330 core
layout (location = 0) in vec4 position;
layout (location = 1) in vec2 tc;
uniform mat4 pr_matrix;
out DATA
{
vec2 tc;
} vs_out;
void main()
{
gl_Position = pr_matrix * position;
vs_out.tc = tc;
}
求大佬帮忙看看,所有代码都在这里了,运行出来结果如下:
但期望中应该左边显示图片
你好,我是有问必答小助手,非常抱歉,本次您提出的有问必答问题,目前超出我们的服务范围,暂时无法为您解答。
首次提问人员可免费体验一次有问必答服务。目前首次提问的问题服务范围为:编程语言、Java开发、python、数据库、前端开发 领域专业技术问题,为您提供问题的解决思路和指导。不提供源码代写、项目文档代写、论文代写、安装包资源发送或安装、软件使用指导等服务。
我们后续会持续优化,扩大我们的服务范围,为您带来更好地服务。