Render terrains

This commit is contained in:
Sebastian 2023-08-24 14:31:46 +02:00
parent 5912ac5a7c
commit 7edcda998c
11 changed files with 354 additions and 77 deletions

BIN
client/res/grass.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 MiB

View File

@ -5,7 +5,8 @@ import core.engine.entity.Entity;
import core.engine.entity.Light; import core.engine.entity.Light;
import core.engine.model.RawModel; import core.engine.model.RawModel;
import core.engine.model.TexturedModel; import core.engine.model.TexturedModel;
import core.engine.shader.StaticShader; import core.engine.renderer.MasterRenderer;
import core.engine.terrain.Terrain;
import core.engine.textures.ModelTexture; import core.engine.textures.ModelTexture;
import org.lwjgl.Version; import org.lwjgl.Version;
import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.glfw.GLFWErrorCallback;
@ -21,7 +22,6 @@ import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFW.glfwSwapBuffers; import static org.lwjgl.glfw.GLFW.glfwSwapBuffers;
import static org.lwjgl.opengl.GL11.glClearColor; import static org.lwjgl.opengl.GL11.glClearColor;
import static org.lwjgl.opengl.GL11C.*;
import static org.lwjgl.system.MemoryStack.stackPush; import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.NULL; import static org.lwjgl.system.MemoryUtil.NULL;
@ -142,12 +142,19 @@ public class Engine {
ModelTexture modelTexture = new ModelTexture(loader.loadTexture("white")); ModelTexture modelTexture = new ModelTexture(loader.loadTexture("white"));
TexturedModel texturedModel = new TexturedModel(model, modelTexture); TexturedModel texturedModel = new TexturedModel(model, modelTexture);
Entity entity = new Entity(texturedModel, new Vector3f(0,0,-50), 0,0,0,1); Entity entity = new Entity(texturedModel, new Vector3f(0,0,-50), 0,0,0,1);
//Generate Simple, Flat Terrain
Terrain terrain = new Terrain(0,0, loader, new ModelTexture(loader.loadTexture("grass")));
Terrain terrain1 = new Terrain(1,0, loader, new ModelTexture(loader.loadTexture("grass")));
// Run the rendering loop until the user has attempted to close // Run the rendering loop until the user has attempted to close
// the window or has pressed the ESCAPE key. // the window or has pressed the ESCAPE key.
MasterRenderer renderer = new MasterRenderer(); MasterRenderer renderer = new MasterRenderer();
while ( !glfwWindowShouldClose(window) ) { while ( !glfwWindowShouldClose(window) ) {
entity.increaseRotation(0,1,0); //entity.increaseRotation(0,1,0);
renderer.processEntity(entity); renderer.processTerrain(terrain1);
renderer.processTerrain(terrain);
//renderer.processEntity(entity);
renderer.render(light, camera); renderer.render(light, camera);
glfwSwapBuffers(window); // swap the color buffers glfwSwapBuffers(window); // swap the color buffers

View File

@ -1,45 +0,0 @@
package core.engine;
import core.engine.entity.Camera;
import core.engine.entity.Entity;
import core.engine.entity.Light;
import core.engine.model.TexturedModel;
import core.engine.shader.StaticShader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MasterRenderer {
private StaticShader shader = new StaticShader();
private Renderer renderer = new Renderer(shader);
private Map<TexturedModel, List<Entity>> entities = new HashMap<>();
public void render(Light light, Camera camera) {
renderer.prepare();
shader.start();
shader.loadLight(light);
shader.loadViewMatrix(camera);
renderer.render(entities);
shader.stop();
entities.clear();
}
public void processEntity(Entity entity) {
TexturedModel entityModel = entity.getModel();
List<Entity> batch = entities.get(entityModel);
if(batch != null) {
batch.add(entity);
} else {
List<Entity> newBatch = new ArrayList<>();
newBatch.add(entity);
entities.put(entityModel, newBatch);
}
}
public void cleanUp() {
shader.cleanUp();
}
}

View File

@ -4,9 +4,9 @@ import utils.vectors.Vector3f;
public class Camera { public class Camera {
private Vector3f position = new Vector3f(0,0,0); private Vector3f position = new Vector3f(0,10,0);
private float pitch; private float pitch;
private float yaw; private float yaw = 180;
private float roll; private float roll;
public void moveLeft() { public void moveLeft() {

View File

@ -1,4 +1,4 @@
package core.engine; package core.engine.renderer;
import core.engine.entity.Entity; import core.engine.entity.Entity;
import core.engine.model.RawModel; import core.engine.model.RawModel;
@ -17,28 +17,22 @@ import java.util.Map;
import static org.lwjgl.opengl.GL11C.*; import static org.lwjgl.opengl.GL11C.*;
public class Renderer { public class EntityRenderer {
private static final float FOV = 70; private static final float FOV = 70;
private static final float NEAR_PLEANE = 0.1f; private static final float NEAR_PLEANE = 0.1f;
private static final float FAR_PLANE = 1000; private static final float FAR_PLANE = 1000;
private Matrix4f projectionMatrix;
private StaticShader shader; private StaticShader shader;
public Renderer(StaticShader shader) { public EntityRenderer(StaticShader shader, Matrix4f projectionMatrix) {
this.shader = shader; this.shader = shader;
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glCullFace(GL11.GL_BACK);
createProjectionMatrix();
shader.start(); shader.start();
shader.loadProjectionMatrix(projectionMatrix); shader.loadProjectionMatrix(projectionMatrix);
shader.stop(); shader.stop();
} }
public void prepare() {
GL11.glEnable(GL11.GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
GL11.glClearColor(5,195,221,1);
}
public void render(Map<TexturedModel, List<Entity>> entities) { public void render(Map<TexturedModel, List<Entity>> entities) {
for(TexturedModel model: entities.keySet()) { for(TexturedModel model: entities.keySet()) {
@ -75,19 +69,5 @@ public class Renderer {
shader.loadTransformationMatrix(transformationMatrix); shader.loadTransformationMatrix(transformationMatrix);
} }
private void createProjectionMatrix() {
float aspectRatio = (float) Engine.WINDOW_WIDTH / (float) Engine.WINDOW_HEIGHT;
float y_scale = (float) ((1f / Math.tan(Math.toRadians(FOV / 2f))) * aspectRatio);
float x_scale = y_scale / aspectRatio;
float frustum_length = FAR_PLANE - NEAR_PLEANE;
projectionMatrix = new Matrix4f();
projectionMatrix.m00 = x_scale;
projectionMatrix.m11 = y_scale;
projectionMatrix.m22 = -((FAR_PLANE + NEAR_PLEANE) / frustum_length);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * NEAR_PLEANE * FAR_PLANE) / frustum_length);
projectionMatrix.m33 = 0;
} }
}

View File

@ -0,0 +1,100 @@
package core.engine.renderer;
import core.engine.Engine;
import core.engine.entity.Camera;
import core.engine.entity.Entity;
import core.engine.entity.Light;
import core.engine.model.TexturedModel;
import core.engine.shader.StaticShader;
import core.engine.shader.TerrainShader;
import core.engine.terrain.Terrain;
import org.lwjgl.opengl.GL11;
import utils.vectors.Matrix4f;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.lwjgl.opengl.GL11C.*;
public class MasterRenderer {
private static final float FOV = 70;
private static final float NEAR_PLEANE = 0.1f;
private static final float FAR_PLANE = 1000;
private StaticShader shader = new StaticShader();
private TerrainShader terrainShader = new TerrainShader();
private EntityRenderer renderer;
private TerrainRenderer terrainRenderer;
private Map<TexturedModel, List<Entity>> entities = new HashMap<>();
private List<Terrain> terrains = new ArrayList<>();
private Matrix4f projectionMatrix;
public MasterRenderer() {
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glCullFace(GL11.GL_BACK);
createProjectionMatrix();
renderer = new EntityRenderer(shader, projectionMatrix);
terrainRenderer = new TerrainRenderer(terrainShader, projectionMatrix);
}
public void prepare() {
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glClearColor(0.49f, 89f, 0.98f, 1);
}
public void render(Light light, Camera camera) {
prepare();
shader.start();
shader.loadLight(light);
shader.loadViewMatrix(camera);
renderer.render(entities);
shader.stop();
terrainShader.start();
terrainShader.loadLight(light);
terrainShader.loadViewMatrix(camera);
terrainRenderer.render(terrains);
terrainShader.stop();
terrains.clear();
entities.clear();
}
public void processTerrain(Terrain terrain) {
terrains.add(terrain);
}
public void processEntity(Entity entity) {
TexturedModel entityModel = entity.getModel();
List<Entity> batch = entities.get(entityModel);
if(batch != null) {
batch.add(entity);
} else {
List<Entity> newBatch = new ArrayList<>();
newBatch.add(entity);
entities.put(entityModel, newBatch);
}
}
public void cleanUp() {
shader.cleanUp();
terrainShader.cleanUp();
}
private void createProjectionMatrix() {
float aspectRatio = (float) Engine.WINDOW_WIDTH / (float) Engine.WINDOW_HEIGHT;
float y_scale = (float) ((1f / Math.tan(Math.toRadians(FOV / 2f))) * aspectRatio);
float x_scale = y_scale / aspectRatio;
float frustum_length = FAR_PLANE - NEAR_PLEANE;
projectionMatrix = new Matrix4f();
projectionMatrix.m00 = x_scale;
projectionMatrix.m11 = y_scale;
projectionMatrix.m22 = -((FAR_PLANE + NEAR_PLEANE) / frustum_length);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = -((2 * NEAR_PLEANE * FAR_PLANE) / frustum_length);
projectionMatrix.m33 = 0;
}
}

View File

@ -0,0 +1,59 @@
package core.engine.renderer;
import core.engine.entity.Entity;
import core.engine.model.RawModel;
import core.engine.model.TexturedModel;
import core.engine.shader.TerrainShader;
import core.engine.terrain.Terrain;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import utils.MatrixGraphicUtils;
import utils.vectors.Matrix4f;
import utils.vectors.Vector3f;
import java.util.List;
public class TerrainRenderer {
private TerrainShader shader;
public TerrainRenderer(TerrainShader shader, Matrix4f projectionMatrix) {
this.shader = shader;
shader.start();
shader.loadProjectionMatrix(projectionMatrix);
shader.stop();
}
public void render(List<Terrain> terrains) {
for(Terrain terrain : terrains) {
prepareTerrain(terrain);
loadModelMatrix(terrain);
GL11.glDrawElements(GL11.GL_TRIANGLES, terrain.getModel().getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
unbindTexturedModel();
}
}
private void prepareTerrain(Terrain model) {
RawModel rawModel = model.getModel();
GL30.glBindVertexArray(rawModel.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
GL20.glEnableVertexAttribArray(2);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, model.getTexture().getTextureID());
}
private void unbindTexturedModel() {
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);
GL30.glBindVertexArray(0);
}
private void loadModelMatrix(Terrain terrain) {
Matrix4f transformationMatrix = MatrixGraphicUtils.createTransformationMatrix(new Vector3f(terrain.getX(), 0, terrain.getZ()), 0,0,0,1);
shader.loadTransformationMatrix(transformationMatrix);
}
}

View File

@ -0,0 +1,54 @@
package core.engine.shader;
import core.engine.entity.Camera;
import core.engine.entity.Light;
import utils.MatrixGraphicUtils;
import utils.vectors.Matrix4f;
public class TerrainShader extends ShaderProgram{
private static final String VERTEX_FILE = "src/main/java/core/engine/shader/terrainVertexShader.glsl";
private static final String FRAGMENT_FILE = "src/main/java/core/engine/shader/terrainFragmentShader.glsl";
private int location_transformationMatrix;
private int location_projectionMatrix;
private int location_viewMatrix;
private int location_lightPosition;
private int location_lightColor;
public TerrainShader() {
super(VERTEX_FILE, FRAGMENT_FILE);
}
@Override
protected void getAllUniformLocations() {
this.location_transformationMatrix = super.getUniformLocation("transformationMatrix");
this.location_projectionMatrix = super.getUniformLocation("projectionMatrix");
this.location_viewMatrix = super.getUniformLocation("viewMatrix");
this.location_lightColor = super.getUniformLocation("lightColor");
this.location_lightPosition = super.getUniformLocation("lightPosition");
}
@Override
protected void bindAttributes() {
super.bindAttribute(0, "position");
super.bindAttribute(1, "textureCoords");
super.bindAttribute(2, "normal");
}
public void loadTransformationMatrix(Matrix4f matrix) {
super.loadMatrix(location_transformationMatrix, matrix);
}
public void loadProjectionMatrix(Matrix4f matrix) {
super.loadMatrix(location_projectionMatrix, matrix);
}
public void loadViewMatrix(Camera camera) {
Matrix4f viewMatrix = MatrixGraphicUtils.createViewMatrix(camera);
super.loadMatrix(location_viewMatrix, viewMatrix);
}
public void loadLight(Light light) {
super.loadVector(location_lightPosition, light.getPosition());
super.loadVector(location_lightColor, light.getColor());
}
}

View File

@ -0,0 +1,21 @@
#version 400 core
in vec2 pass_textureCoords;
in vec3 surfaceNormal;
in vec3 toLightVector;
out vec4 out_Color;
uniform sampler2D textureSampler;
uniform vec3 lightColor;
void main(void) {
vec3 unitNormal = normalize(surfaceNormal);
vec3 unitToLightVector = normalize(toLightVector);
float nDotl = dot(unitNormal, unitToLightVector);
float brightness = max(nDotl, 0.2);
vec3 diffusde = brightness * lightColor;
out_Color = vec4(diffusde, 1.0) * texture(textureSampler, pass_textureCoords);
}

View File

@ -0,0 +1,24 @@
#version 400 core
in vec3 position;
in vec2 textureCoords;
in vec3 normal;
uniform mat4 transformationMatrix;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform vec3 lightPosition;
out vec2 pass_textureCoords;
out vec3 surfaceNormal;
out vec3 toLightVector;
void main(void) {
vec4 worldPosition = transformationMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * viewMatrix * worldPosition;
pass_textureCoords = textureCoords * 40;
surfaceNormal = (transformationMatrix * vec4(normal, 0.0)).xyz;
toLightVector = lightPosition - worldPosition.xyz;
}

View File

@ -0,0 +1,77 @@
package core.engine.terrain;
import core.engine.Loader;
import core.engine.model.RawModel;
import core.engine.textures.ModelTexture;
public class Terrain {
private static final float SIZE = 800;
private static final int VERTEX_COUNT = 128;
private float x;
private float z;
private RawModel model;
private ModelTexture texture;
public Terrain(int gridX, int gridZ, Loader loader, ModelTexture modelTexture) {
this.texture = modelTexture;
this.x = gridX * SIZE;
this.z = gridZ * SIZE;
this.model = generateTerrain(loader);
}
private RawModel generateTerrain(Loader loader){
int count = VERTEX_COUNT * VERTEX_COUNT;
float[] vertices = new float[count * 3];
float[] normals = new float[count * 3];
float[] textureCoords = new float[count*2];
int[] indices = new int[6*(VERTEX_COUNT-1)*(VERTEX_COUNT-1)];
int vertexPointer = 0;
for(int i=0;i<VERTEX_COUNT;i++){
for(int j=0;j<VERTEX_COUNT;j++){
vertices[vertexPointer*3] = (float)j/((float)VERTEX_COUNT - 1) * SIZE;
vertices[vertexPointer*3+1] = 0;
vertices[vertexPointer*3+2] = (float)i/((float)VERTEX_COUNT - 1) * SIZE;
normals[vertexPointer*3] = 0;
normals[vertexPointer*3+1] = 1;
normals[vertexPointer*3+2] = 0;
textureCoords[vertexPointer*2] = (float)j/((float)VERTEX_COUNT - 1);
textureCoords[vertexPointer*2+1] = (float)i/((float)VERTEX_COUNT - 1);
vertexPointer++;
}
}
int pointer = 0;
for(int gz=0;gz<VERTEX_COUNT-1;gz++){
for(int gx=0;gx<VERTEX_COUNT-1;gx++){
int topLeft = (gz*VERTEX_COUNT)+gx;
int topRight = topLeft + 1;
int bottomLeft = ((gz+1)*VERTEX_COUNT)+gx;
int bottomRight = bottomLeft + 1;
indices[pointer++] = topLeft;
indices[pointer++] = bottomLeft;
indices[pointer++] = topRight;
indices[pointer++] = topRight;
indices[pointer++] = bottomLeft;
indices[pointer++] = bottomRight;
}
}
return loader.loadToVAO(vertices, textureCoords, normals, indices);
}
public float getX() {
return x;
}
public float getZ() {
return z;
}
public RawModel getModel() {
return model;
}
public ModelTexture getTexture() {
return texture;
}
}