Opengl terrain texture shading - java

I am currently working on an OpenGL (JOGL) project in java.
The goal is to create a terrain with textures and shading.
I'm creating a random simplex noise, using these values as a heightmap.
The heights are mapped to a 1D texture to simulate coloring based on height.
A material (ambient/diffuse/specular/shininess) is then used to simulate shading.
However; after adding shading to the terrain, 'stripes' appear on each 'column' (Y direction) of the terrain.
The following material is then applied:
TERRAIN(
new float[]{0.5f, 0.5f, 0.5f, 1.0f},
new float[]{0.7f, 0.7f, 0.7f, 1.0f},
new float[]{0.2f, 0.2f, 0.2f, 1.0f},
new float[]{100f})
The material enum constructor:
Material(float[] ambient, float[] diffuse, float[] specular, float[] shininess) {
this.ambient = ambient;
this.diffuse = diffuse;
this.specular = specular;
this.shininess = shininess;
}
I apply the material using the following method:
public void use(GL2 gl) {
// set the material properties
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_AMBIENT, ambient, 0);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_DIFFUSE, diffuse, 0);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, specular, 0);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SHININESS, shininess, 0);
}
After creating a 2D 'noisearray' consistent of 0-1 values, an 2D vectorarray is created, consiting of X*Y vectors, where each vector represents a point in the plane/terrain.
Here is the method that draws triangles in between those points, where you can see I draw the plane per column (Y direction):
public void draw(GL2 gl, GLU glu, GLUT glut, Drawer drawer) {
Material.TERRAIN.use(gl);
texture.bind(gl);
if (showGrid)
gl.glPolygonMode( gl.GL_FRONT_AND_BACK, gl.GL_LINE );
ArrayList<Vector[]> normals = new ArrayList<>();
for(int i=1;i<vectors.length;i++) {
gl.glBegin(gl.GL_TRIANGLE_STRIP);
for (int j = 0; j < vectors[i].length; j++) {
Vector normalTopRight, normalBottomLeft;
//Calculate normals top right
Vector v1, v2, triangleCenterTR;
if (j < vectors[i].length - 1)
{
v1 = vectors[i-1][j].subtract(vectors[i][j]);
v2 = vectors[i][j+1].subtract(vectors[i][j]);
normalTopRight = v2.cross(v1).normalized();
// Get center (a+b+c)*(1/3)
triangleCenterTR = (vectors[i][j].add(vectors[i - 1][j]).add(vectors[i][j + 1])).scale(1.0 / 3);
} else {
v1 = vectors[i-1][j].subtract(vectors[i][j]);
v2 = vectors[i][j-1].subtract(vectors[i][j]);
normalTopRight = v1.cross(v2).normalized();
// Get center (a+b+c)*(1/3)
triangleCenterTR = (vectors[i][j].add(vectors[i-1][j]).add(vectors[i][j-1])).scale(1.0/3);
}
normals.add(new Vector[] {triangleCenterTR, triangleCenterTR.add(normalTopRight)});
if (j != 0)
{
v1 = vectors[i][j].subtract(vectors[i-1][j]);
v2 = vectors[i-1][j-1].subtract(vectors[i-1][j]);
normalBottomLeft = v2.cross(v1).normalized();
// Get center (a+b+c)*(1/3)
Vector triangleCenterBL = (vectors[i - 1][j].add(vectors[i][j]).add(vectors[i - 1][j - 1])).scale(1.0 / 3);
normals.add(new Vector[]{triangleCenterBL, triangleCenterBL.add(normalBottomLeft)});
} else {
normalBottomLeft = null; // If j==0, there is no bottom left triangle above
}
/**
* We have everything to start drawing
*/
// Set some color
if (j == 0) {
// Initialization vector
gl.glTexCoord1d(mapTextureToHeight(vectors[i][j].z));
drawer.glVertexV(vectors[i][j]);
} else {
drawer.glNormalV(normalBottomLeft);
}
// Shift left
gl.glTexCoord1d(mapTextureToHeight(vectors[i - 1][j].z));
drawer.glVertexV(vectors[i - 1][j]);
// Right down diagonally
if (j < vectors[i].length - 1) { // Skip if we are reached the end
gl.glTexCoord1d(mapTextureToHeight(vectors[i][j + 1].z));
drawer.glNormalV(normalTopRight);
drawer.glVertexV(vectors[i][j + 1]);
}
}
gl.glEnd();
}
if (showGrid)
gl.glPolygonMode( gl.GL_FRONT_AND_BACK, gl.GL_FILL );
if (drawNormals) {
for (Vector[] arrow : normals) {
if (yellowNormals)
Material.YELLOW.use(gl);
else
gl.glTexCoord1d(mapTextureToHeight(arrow[0].z));
drawer.drawArrow(arrow[0], arrow[1], 0.05);
}
}
texture.unbind(gl);
}
The most obvious reason for the stripes is the fact I draw the triangles per column, causing OpenGL to not be able to smoothen the shading on the polygons (GL_SMOOTH). Is there any way to fix this?
[Edit1] Copied from your comment by Spektre
I just finished calculating the average normals, I indeed have a smooth terrain now, but the lighting looks kind of dull (no depth)
Here is the new code that draws the terrain:
public void draw() {
if (showGrid)
gl.glPolygonMode( gl.GL_FRONT_AND_BACK, gl.GL_LINE);
texture.bind(gl);
Material.TERRAIN.use(gl);
for(int i=1;i<vectors.length;i++) {
gl.glBegin(gl.GL_TRIANGLE_STRIP);
for (int j = 0; j < vectors[i].length; j++) {
// Initialization vector
gl.glTexCoord1d(mapTextureToHeight(vectors[i][j].z));
drawer.glNormalV(normals.get(vectors[i][j]));
drawer.glVertexV(vectors[i][j]);
// Shift left
gl.glTexCoord1d(mapTextureToHeight(vectors[i - 1][j].z));
drawer.glNormalV(normals.get(vectors[i - 1][j]));
drawer.glVertexV(vectors[i - 1][j]);
}
gl.glEnd();
}
if (showGrid)
gl.glPolygonMode( gl.GL_FRONT_AND_BACK, gl.GL_FILL );
if (drawNormals)
drawFaceNormals();
texture.unbind(gl);
}
I cleaned it up, I am sure the normals are pointing the correct way using the drawnormals function and made sure OpenGL is seeing the top of the terrain as FRONT using (gl.GL_FRONT -> draws only above terrain, not below).
Here is the complete class: PasteBin

Thanks to #Spektre for helping me out.
After properly calculating the average normal of all surrounding faces on a vertex and using this normal for glNormal, the shading was correct.

Related

Java + OpenGL: Texture Z-fighting in batch rendering

The main issue:
Good afternoon, everyone. I have such a question, when I use more than two textures in batch rendering, when using 2 textures (that is, my downloaded from disk), it starts glitching with the first texture (white), which in theory should not be because when the second texture is used from the texture slot, it becomes the main one for rendering. And this happens only in the perspective projection, everything is fine with the orthographic projection (most likely because it does not display the distance between objects and the Z-coordinate).
Here is a couple images of the issue:
Video
Google Drive
The whole problem is that I have a lot of code written and I think it will not make sense to throw off all of it. I will still show some pieces, but I would like you to express your assumption why this may happen. Maybe it's because I'm binding several texture slots at once. In general, I will be grateful to every advice or answer if it turns out.
Draw Sprite using batch rendering (Example)
public static void drawRectnagle(GameObject rect) {
var transform = rect.getComponent(Transform.class);
var spriteRenderer = rect.getComponent(SpriteRenderer.class);
// Begin new batch if in this batch not has room left.
if (batch.indexCount >= Batch.maxIndices)
newBatch();
int textureIndex = 0;
Vector2f[] textureCoords = batch.textureCoords;
Texture texture = null;
Vector4f color = null;
if(spriteRenderer != null) {
texture = spriteRenderer.texture;
color = spriteRenderer.color;
} else
color = new Vector4f(1, 1, 1, 1);
if(texture != null)
textureCoords = texture.getTextureCoords();
else
texture = batch.textures[0];
for (int i = 1; i < batch.textureIndex; i++) {
if (batch.textureIndex >= Batch.maxTextures)
newBatch();
// Equal texture by OpenGL ID's.
if (batch.textures[i].equals(texture)) {
textureIndex = i;
break;
}
}
if (textureIndex == 0.0f) {
textureIndex = batch.textureIndex;
batch.textures[batch.textureIndex] = texture;
batch.textureIndex++;
}
...
// Loading vertex data to batch.
...
}
Rendering batch after setting sprites data (Example)
private static void endBatch() {
if (batch.indexCount == 0)
return;
int sizeBytes = sizeof(Vertex.class); // NOTE: Size of Vertex class is currect here.
int size = batch.vertexIndex * sizeBytes;
batch.vertexBuffer.putData(batch.vertex, size, batch.vertexIndex, sizeBytes / 4);
// Bind each texture from slot.
for (int i = 0; i < batch.textureIndex; i++)
batch.textures[i].bind(i);
RenderEngine.EnableAlphaBlending();
RenderEngine.EnableDepthTesting();
glBindVertexArray(batch.vertexArray.get());
glDrawElements(GL_TRIANGLES, batch.vertexArray.getIndexBuffer().getCount(), GL_UNSIGNED_INT, 0);
RenderEngine.DisableAlphaBlending();
RenderEngine.DisableDepthTesting();
}
And if someone interest how my putData method works:
public void putData(Vertex[] vertex, int size, int index, int elementCount) {
//Convert all Vertex data to single float[] to pass as data in vertex buffer.
float[] data = ...
if(data != null) {
var pData = memAlloc(size).asFloatBuffer().put(data).flip();
glBindBuffer(GL_ARRAY_BUFFER, handle);
nglBufferData(GL_ARRAY_BUFFER, size, memAddress(pData), getOpenGLUsage(usage));
memFree(pData);
}
}
Fragment Shader
#version 450 core
layout (location = 0) out vec4 out_Pixel;
in vec3 position;
in vec4 color;
in vec2 textureCoord;
in float textureIndex;
uniform sampler2D u_Textures[32];
void main() {
vec4 texColour = vec4(1, 1, 1, 1);
texColour *= texture(u_Textures[int(textureIndex)], textureCoord);
vec4 finalColor = color * texColour;
if(finalColor.a < 0.1)
discard;
out_Pixel = finalColor;
}

How should I be implementing a view clipping plane in a 3D Engine?

This project is written entirely from scratch in Java. I've just been bored ever since Covid started, so I wanted something that would take up my time, and teach me something cool. I've been stuck on this problem for about a week now though. When I try to use my near plane clipping method it skews the new vertices to the opposite side of the screen, but sometimes times it works just fine.
Failure Screenshot
Success Screenshot
So my thought is maybe that since it works sometimes, I'm just not doing the clipping at the correct time in the pipeline?
I start by face culling and lighting,
Then I apply a Camera View Transformation to the Vertices,
Then I clip on the near plane
Finally I apply the projection matrix and Clip any remaining off screen Triangles
Code:
This calculates the intersection points. Sorry if it's messy or to long I'm not very experienced in coding, my major is physics, not CS.
public Vertex vectorIntersectPlane(Vector3d planePos, Vector3d planeNorm, Vector3d lineStart, Vector3d lineEnd){
float planeDot = planeNorm.dotProduct(planePos);
float startDot = lineStart.dotProduct(planeNorm);
float endDot = lineEnd.dotProduct(planeNorm);
float midPoint = (planeDot - startDot) / (endDot - startDot);
Vector3d lineStartEnd = lineEnd.sub(lineStart);
Vector3d lineToIntersect = lineStartEnd.scale(midPoint);
return new Vertex(lineStart.add(lineToIntersect));
}
public float distanceFromPlane(Vector3d planePos, Vector3d planeNorm, Vector3d vert){
float x = planeNorm.getX() * vert.getX();
float y = planeNorm.getY() * vert.getY();
float z = planeNorm.getZ() * vert.getZ();
return (x + y + z - (planeNorm.dotProduct(planePos)));
}
//When a triangle gets clipped it has 4 possible outcomes
// 1 it doesn't actually need clipping and gets returned
// 2 it gets clipped into 1 new triangle, for testing these are red
// 3 it gets clipped into 2 new triangles, for testing 1 is green, and 1 is blue
// 4 it is outside the view planes and shouldn't be rendered
public void clipTriangles(){
Vector3d planePos = new Vector3d(0, 0, ProjectionMatrix.fNear, 1f);
Vector3d planeNorm = Z_AXIS.clone();
final int length = triangles.size();
for(int i = 0; i < length; i++) {
Triangle t = triangles.get(i);
if(!t.isDraw())
continue;
Vector3d[] insidePoint = new Vector3d[3];
int insidePointCount = 0;
Vector3d[] outsidePoint = new Vector3d[3];
int outsidePointCount = 0;
float d0 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[0]);
float d1 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[1]);
float d2 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[2]);
//Storing distances from plane and counting inside outside points
{
if (d0 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[0];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[0];
outsidePointCount++;
}
if (d1 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[1];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[1];
outsidePointCount++;
}
if (d2 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[2];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[2];
}
}
//Triangle has 1 point still inside view, remove original triangle add new clipped triangle
if (insidePointCount == 1) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[1]);
vertices.add(newVert1);
vertices.add(newVert2);
//Triangles are stored with vertex references instead of the actual vertex object.
Triangle temp = new Triangle(t.getVertKeys()[0], vertices.size() - 2, vertices.size() - 1, vertices);
temp.setColor(1,0,0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
//Triangle has two points inside remove original add two new clipped triangles
if (insidePointCount == 2) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[1], outsidePoint[0]);
vertices.add(newVert1);
vertices.add(newVert2);
Triangle temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 1, vertices);
temp.setColor(0, 1, 0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 2, vertices);
temp.setColor(0, 0, 1, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
}
}
I figured out the problem, The new clipped triangles were not being given the correct vertex references. they were just being given the first vertex of the triangle irregardless of if that was inside the view or not.

Adding thickness to a 2D sprite when turning

In my game, my entities turn like a piece of paper, as shown here at half-speed: https://imgur.com/a/u2suen6
I want to give the entities a bit of thickness when they turn, making them more cardboard-thin than paper-thin.
I thought about using a Pixmap to detect and extend the edge pixels and give the image some Three-Dimensionality. I also considered duplicating the image along the x-axis to give the same effect. Of the two ideas, the Pixmap holds out the most promise in my mind. However, I'm wondering if there's a better solution.
I'm using a GLSL shader to give the entities highlights and shadows while turning, as you saw in the gif. I think that with the right knowledge, I could achieve what I'm going for using the same shader program.
My shader looks like this:
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform vec2 u_resolution;
uniform vec3 color;
void main()
{
vec4 col = vec4(color, 0.0);
gl_FragColor = texture2D(u_texture, v_texCoords) * v_color + col;
}
I think that one might be able to make calculations based on the uniform vec3 color that I pass it (with its values ranging from 0, 0, 0 to 1, 1, 1. 1's being highlight and 0's being shadow). Unfortunately, I don't have the understanding of shaders to do so.
If any of you have the know-how, could you steer me in the right direction? Or let me know if I should just stick to the Pixmap idea.
Edit: I'm trying to stay away from using a 3D model because I'm 6.4k lines of code deep using a 2d Orthographic Camera.
Edit 2: I figured that the reflection shader wouldn't look good if I tried making the sprite look 3D. I scrapped the shader, went with the Pixmap idea, and plan on implementing shadows and reflections to the pixmap without any shader. Though it looks good so far without reflections.
I ended up going with my pixmap idea. I want to share my code so that others can know how I got 2D thickness to work.
Please note that in the following code:
dir is a floating point value in the range -1.0 to 1.0. It tells the program where the sprite is in its turn. -1 means facing fully left. 1 meaning right. 0 means that it's 'facing' the camera.
right is a boolean that tells the program which direction the entity is turning. true means that the entity is turning from left to right. false means from right to left.
The Code:
private Texture getTurningImage(TextureRegion input, int thickness)
{
if(Math.abs(dir) < 0.1)
dir = (right ? 1 : -1) * 0.1f;
Texture texture = input.getTexture();
if (!texture.getTextureData().isPrepared())
{
texture.getTextureData().prepare();
}
Pixmap pixmap = texture.getTextureData().consumePixmap();
Pixmap p = new Pixmap(64, 64, Pixmap.Format.RGBA8888);
p.setFilter(Pixmap.Filter.NearestNeighbour);
Pixmap texCopy = new Pixmap(input.getRegionWidth(), input.getRegionHeight(), Pixmap.Format.RGBA8888);
// getting a texture out of the input region. I can't use input.getTexture()
// because it's an animated sprite sheet
for (int x = 0; x < input.getRegionWidth(); x++)
{
for (int y = 0; y < input.getRegionHeight(); y++)
{
int colorInt = pixmap.getPixel(input.getRegionX() + x, input.getRegionY() + y);
Color c = new Color(colorInt);
colorInt = Color.rgba8888(c);
texCopy.drawPixel(x, y, colorInt);
}
}
pixmap.dispose();
float offsetVal = Math.round(thickness/2.0) * (float) -Math.cos((dir * Math.PI)/2);
if(offsetVal > -1.23/Math.pow(10, 16))
{
offsetVal = 0;
}
// generate the pixel colors we'll use for the side view
Pixmap sideProfile = new Pixmap(1, 64, Pixmap.Format.RGBA8888);
for (int y = 0; y < texCopy.getHeight(); y++)
{
for (int x = 0; x < texCopy.getWidth(); x++)
{
int colorInt = texCopy.getPixel(x, y);
if(new Color(colorInt).a != 0 && new Color(texCopy.getPixel(x + 1, y)).a == 0)
{
Color c = new Color(colorInt);
c.mul(.8f); // darken the color
c.a = 1;
colorInt = Color.rgba8888(c);
sideProfile.drawPixel(0, y, colorInt);
continue;
}
}
}
// drawing the bottom layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(-offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// drawing the middle (connecting) layer
// based on the edge pixels of the bottom layer, then translated to be in the middle
for (int y = 0; y < p.getHeight(); y++)
{
int colorInt = sideProfile.getPixel(0, y);
for (int x = 0; x < p.getWidth(); x++)
{
if(new Color(p.getPixel(x, y)).a != 0 && new Color(p.getPixel(x + 1, y)).a == 0)
{
for(int i = 0; i <= 2 * Math.round(Math.abs(offsetVal)); i++) // the for the length between the top and bottom
{
p.drawPixel(x + i - 2 * (int)Math.round(Math.abs(offsetVal)), y, colorInt);
}
}
}
}
// drawing the top layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// flip if facing left
if(dir < 0)
{
p = flipPixmap(p);
}
return new Texture(p);
}
My flipPixmap method looks like this (stolen from stack overflow):
private Pixmap flipPixmap(Pixmap src)
{
final int width = src.getWidth();
final int height = src.getHeight();
Pixmap flipped = new Pixmap(width, height, src.getFormat());
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
flipped.drawPixel(x, y, src.getPixel(width - x - 1, y));
}
}
return flipped;
}
Here's the result :D https://imgur.com/a/wGeHg9D

OpenGL GLUT teapot collision with camera

I am making a simple 3D Game in which I am moving in a maze. Now I want to implement collecting teapots which are randomly disturbed in the area. The random drawing of teapots is working perfectly. Now I need to make collisions. For that I would like to use AABB algorithm.
Here is how I initialize coords of teapots so they are disturbed in the maze where isn't wall:
for(int i=0; i<5;i++) {
x = random.nextInt(21);
y = random.nextInt(21);
while(wall[x][y]) {
x = random.nextInt(21);
y = random.nextInt(21);
}
if(!wall[x][y]) {
teapot[x][y] = true;
}
}
Here is how I render teapots:
gl.glLoadIdentity();
//render konviček
for(int i=0;i<21;i++) {
for(int j=0; j<21; j++) {
if(labyrinth.getDonut()[i][j]) {
gl.glTranslatef((float) i, 0f, (float) j);
wallTexture.disable(gl);
gl.glColor3f(1.0f, 0.0f, 0.0f);
glut.glutSolidTeapot(0.2);
gl.glTranslatef((float) -i, 0f, (float) -j);
}
}
}
My question is, how to simply implement the AABB algorithm. I know I am supposed to draw a cube around those teapots, however I can't figure out how to code it.
I already have my checkCollision(double X, double Z); method prepared, so I really just need to draw the box around the teapot and get its coordinates.

Java LWJGL - Heightmap not rendering correctly

I am trying to implement a heightmap into my game following Oskar Veerhoek's youtube tutorials. I follow the steps and I get a few methods:
private static void setUpHeightmap() {
try {
// Load the heightmap-image from its resource file
BufferedImage heightmapImage = ImageIO.read(new File("res/img/heightmap.bmp"));
// Initialise the data array, which holds the heights of the heightmap-vertices, with the correct dimensions
data = new float[heightmapImage.getWidth()][heightmapImage.getHeight()];
// Lazily initialise the convenience class for extracting the separate red, green, blue, or alpha channels
// an int in the default RGB color model and default sRGB colourspace.
Color colour;
// Iterate over the pixels in the image on the x-axis
for (int z = 0; z < data.length; z++) {
// Iterate over the pixels in the image on the y-axis
for (int x = 0; x < data[z].length; x++) {
// Retrieve the colour at the current x-location and y-location in the image
colour = new Color(heightmapImage.getRGB(z, x));
// Store the value of the red channel as the height of a heightmap-vertex in 'data'. The choice for
// the red channel is arbitrary, since the heightmap-image itself only has white, gray, and black.
data[z][x] = colour.getRed();
}
}
// Create an input stream for the 'lookup texture', a texture that will used by the fragment shader to
// determine which colour matches which height on the heightmap
FileInputStream heightmapLookupInputStream = new FileInputStream("res/img/heightmap_lookup.png");
// Create a class that will give us information about the image file (width and height) and give us the
// texture data in an OpenGL-friendly manner
PNGDecoder decoder = new PNGDecoder(heightmapLookupInputStream);
// Create a ByteBuffer in which to store the contents of the texture. Its size is the width multiplied by
// the height and 4, which stands for the amount of bytes a float is in Java.
ByteBuffer buffer = BufferUtils.createByteBuffer(4 * decoder.getWidth() * decoder.getHeight());
// 'Decode' the texture and store its data in the buffer we just created
decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
// Make the contents of the ByteBuffer readable to OpenGL (and unreadable to us)
buffer.flip();
// Close the input stream for the heightmap 'lookup texture'
heightmapLookupInputStream.close();
// Generate a texture handle for the 'lookup texture'
lookupTexture = glGenTextures();
glBindTexture(GL_TEXTURE_2D, lookupTexture);
// Hand the texture data to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, buffer);
} catch (IOException e) {
e.printStackTrace();
}
// Use the GL_NEAREST texture filter so that the sampled texel (texture pixel) is not smoothed out. Usually
// using GL_NEAREST will make the textured shape appear pixelated, but in this case using the alternative,
// GL_LINEAR, will make the sharp transitions between height-colours ugly.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Generate a display list handle for the display list that will store the heightmap vertex data
heightmapDisplayList = glGenLists(1);
// TODO: Add alternative VBO rendering for pseudo-compatibility with version 3 and higher.
glNewList(heightmapDisplayList, GL_COMPILE);
// Scale back the display list so that its proportions are acceptable.
glScalef(0.2f, 0.06f, 0.2f);
// Iterate over the 'strips' of heightmap data.
for (int z = 0; z < data.length - 1; z++) {
// Render a triangle strip for each 'strip'.
glBegin(GL_TRIANGLE_STRIP);
for (int x = 0; x < data[z].length; x++) {
// Take a vertex from the current strip
glVertex3f(x, data[z][x], z);
// Take a vertex from the next strip
glVertex3f(x, data[z + 1][x], z + 1);
}
glEnd();
}
glEndList();
}
private static void setUpShaders() {
shaderProgram = ShaderLoader.loadShaderPair("res/shaders/landscape.vs", "res/shaders/landscape.fs");
glUseProgram(shaderProgram);
// The following call is redundant because the default value is already 0, but illustrates how you would use
// multiple textures
glUniform1i(glGetUniformLocation(shaderProgram, "lookup"), 0);
}
private static void setUpStates() {
camera.applyOptimalStates();
glPointSize(2);
// Enable the sorting of shapes from far to near
glEnable(GL_DEPTH_TEST);
// Set the background to a blue sky colour
glClearColor(0, 0.75f, 1, 1);
// Remove the back (bottom) faces of shapes for performance
//glEnable(GL_CULL_FACE);
}
When I run the game, the heightmap doesn't show, and my models on the screen get squished like this:
Im thinking it has something to do with it incorrectly rendering the heights and applying it to my models. But I really don't know as I am VERY foreign to 3D development. Some help would be amazing! Thanks!
To get the heightmap information from the image, you simply code:
try {
BufferedImage height = ImageIO.read(new File("utah.jpg"));
hm = new double[height.getHeight()][height.getWidth()];
for (int row = 0; row < height.getHeight(); row++) {
for (int col = 0; col < height.getWidth(); col++) {
hm[row][col] = (height.getRGB(col, row) & 255) / 255.0;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
To draw the points, you just do the following(translate from JOGL to LWJGL):
gl.glPushMatrix();
for (int x = 0; x < (257 - 1); x++) {
gl.glBegin(GL.GL_TRIANGLE_STRIP);
for (int z = 0; z < (257 - 1); z++) {
double[] row = hm[0];
double y = hm[(x * hm.length) % 10][(z + row.length) % 10]
* 5;
gl.glColor3d(1, 1, 1);
gl.glVertex3d(x, y, z);
//gl.glTexCoord3d(x, y, z);
y = hm[((x + 1) * hm.length) % 10][((z + 1) + row.length) % 10]
* 5;
gl.glColor3d(1, 1, 1);
gl.glVertex3d(x + 1, y, z + 1);
//gl.glTexCoord3d(x + 1, y, z + 1);
}
gl.glEnd();
}
gl.glPopMatrix();

Categories