When I try to render my vbo with a directional light shader, I noticed that the color section of the vbo was messing with the shading.
This is the format of my vbo (x,y,z,w ,r,g,b,a ,u,v ,nx,ny,nz)
The vertex color vertex points, normals, and uv coords are stored in an array, like this
float[] array = new float {
0f,0f,0f,0f,
so on and so on...
}
Here is the vertex and fragment shader code
#version 150 core
//VERTEX SHADER
in vec4 in_Position;
in vec4 in_Color;
in vec2 in_TextureCoord;
in vec3 in_Normal;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat3 normal;
out Data {
float intensity;
vec2 st;
} Pass;
void main(void) {
vec3 l_dir = normalize(mat3(model) * vec3(0,0,1));
vec3 n = normalize(normal * in_Normal);
Pass.intensity = max(dot(n, l_dir), 0.0);
Pass.st = in_TextureCoord;
gl_Position = projection * view * model * in_Position;
}
#version 150 core
//FRAGMENT SHADER
uniform sampler2D texture_diffuse;
in Data {
float intensity;
vec2 st;
} Pass;
out vec4 color;
void main(void) {
vec4 diffuse = texture(texture_diffuse, Pass.st);
vec4 test = vec4(1);
color = Pass.intensity * diffuse;
}
The code that builds the vbo
variables: v = vertex points, c = color data, st = texture coords, n = normal vectors.
classes: VertexData = class for organizing vertex data (the code for the VertexData class will be after this class)
public void construct() {
Manager.print(getClass(), "Normals" + Arrays.toString(n));
int p = 0;
int o = 0;
int q = 0;
vertices = new VertexData[v.length/4];
for (int j=0;j<vertices.length;j++) {
vertices[j] = new VertexData();
vertices[j].setXYZW(v[p], v[p+1], v[p+2], v[p+3]);
vertices[j].setRGBA(c[p], c[p+1], c[p+2], c[p+3]);
vertices[j].setST(st[o], st[o+1]);
vertices[j].setNormal(n[q], n[q+1], n[q+2]);
p += 4;
q += 3;
o += 2;
}
FloatBuffer vBuffer = BufferUtils.createFloatBuffer(vertices.length * VertexData.elementCount);
for (VertexData vertex : vertices) {
vBuffer.put(vertex.getElements());
}
vBuffer.flip();
IntBuffer iBuffer = BufferUtils.createIntBuffer(i.length);
iBuffer.put(i);
iBuffer.flip();
vaoId = glGenVertexArrays();
glBindVertexArray(vaoId);
vboId = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, vBuffer, GL_STATIC_DRAW);
glVertexAttribPointer(0, VertexData.pec, GL_FLOAT,
false, VertexData.stride, VertexData.pbo);
glVertexAttribPointer(1, VertexData.cec, GL_FLOAT,
false, VertexData.stride, VertexData.cbo);
glVertexAttribPointer(2, VertexData.tec, GL_FLOAT,
false, VertexData.stride, VertexData.tbo);
glVertexAttribPointer(3, VertexData.nec, GL_FLOAT,
false, VertexData.stride, VertexData.nbo);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
vboiId = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboiId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, iBuffer, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
The VertexData class (as seen in the previous code snipet)
public class VertexData {
public VertexData() {}
public void setXYZW(float x, float y, float z, float w) {
xyzw = new float[] {x,y,z,w};
}
public void setRGBA(float r, float g, float b, float a) {
rgba = new float[] {r,g,b,a};
}
public void setST(float s, float t) {
st = new float[] {s,t};
}
public void setNormal(float x, float y, float z) {
norm = new float[] {x,y,z};
}
public float[] getElements() {
float[] out = new float[VertexData.elementCount];
int i = 0;
//XYZW Elements
out[i++] = xyzw[0];
out[i++] = xyzw[1];
out[i++] = xyzw[2];
out[i++] = xyzw[3];
//RGBA Elements
out[i++] = rgba[0];
out[i++] = rgba[1];
out[i++] = rgba[2];
out[i++] = rgba[3];
//ST Elements
out[i++] = st[0];
out[i++] = st[1];
//NORMAL Elements
out[i++] = norm[0];
out[i++] = norm[1];
out[i++] = norm[2];
return out;
}
public float[] getXYZW() {
return new float[] {xyzw[0], xyzw[1], xyzw[2], xyzw[3]};
}
public float[] getRGBA() {
return new float[] {rgba[0], rgba[1], rgba[2], rgba[3]};
}
public float[] getST() {
return new float[] {st[0], st[1]};
}
public float[] getNORM() {
return new float[] {norm[0], norm[1], norm[2]};
}
private float[] xyzw = new float[] {0f, 0f, 0f, 0f};
private float[] rgba = new float [] {0f, 0f, 0f, 0f};
private float[] st = new float[] {0f, 0f};
private float[] norm = new float[] {0f,0f,0f};
private int v,c,t,n;
public static final int elementBytes = 4;
public static final int pec = 4;
public static final int cec = 4;
public static final int tec = 2;
public static final int nec = 3;
public static final int pbc = pec * elementBytes;
public static final int cbc = cec * elementBytes;
public static final int tbc = tec * elementBytes;
public static final int nbc = nec * elementBytes;
public static final int pbo = 0;
public static final int cbo = pbo + pbc;
public static final int tbo = cbo + cbc;
public static final int nbo = tbo + tbc;
public static final int elementCount = pec + cec + tec + nec;
public static final int stride = pbc + cbc + tbc + nbc;
}
Now here are some images and causes of the problem
color array =
float[] {
1,0,0,1,
0,1,0,1,
0,0,1,1,
1,1,1,1
}
result =
now I change the vertex colors, and this happens
color array =
float[] {
0,0,1,1,
0,1,0,1,
1,0,0,1,
1,1,1,1
}
result =
final example
color array =
float[] {
1,1,1,1,
1,1,1,1,
1,1,1,1,
1,1,1,1
}
result = the plane appears to have flat shading even though it should be per vertex. (I don't think I need another picture to explain this.)
I think that the problem is somewhere in the shader or vbo, but I can't be sure.
Related
Using LWJGL3 and JOML.
I am trying to work out how to get the point on the terrain using a raycast system. I use the point to set a characters position to see what point is being outputted.
I don't think it is my terrain causing the issue since moving the character (with the keyboard or by just adding a value each frame) and attaching it to the terrain works fine.
The issues I get:
Inverting the projection matrix causes the point to flicker between the correct position and some other point but I am not sure about the relationship with this other value.
Not inverting the projection matrix stops the flickering but now the point moves away from the mouse position exponentially.
Near the center of the screen the 2 positions will merge together.
If I print out the terrain point vector it comes out in scientific notation for some reason:
( 5.335E+1 3.849E-2 -9.564E+1)
( 8.804E+1 -6.256E-3 -2.815E+2)
( 5.335E+1 3.849E-2 -9.564E+1)
( 8.804E+1 -6.256E-3 -2.815E+2)
( 5.335E+1 3.849E-2 -9.564E+1)
If I print out each of the x, y and z values individually it is actually showing the correct position but is also flicking to another (the difference between which increases the further from the center of the screen that the mouse moves):
5.8912144, 0.016174316, -7.771721
6.1992702, 0.01574707, -11.79966
5.8912144, 0.016174316, -7.771721
6.1992702, 0.01574707, -11.79966
6.609352, 0.01815033, -8.793705
Raycasting class:
public class Raycast
{
private static final int RECURSION_COUNT = 200;
private static final float RAY_RANGE = 600;
private Input input;
private Vector3f currentRay = new Vector3f();
private Matrix4f projectionMatrix;
private Matrix4f viewMatrix;
private Camera camera;
private Terrain terrain;
private Vector3f currentTerrainPoint;
public Raycast(Camera camera, Matrix4f projectionMatrix, Terrain terrain, Input input) {
this.camera = camera;
this.projectionMatrix = projectionMatrix;
this.input = input;
this.viewMatrix = MathUtils.createViewMatrix(camera);
this.terrain = terrain;
}
public void update()
{
viewMatrix = MathUtils.createViewMatrix(camera);
currentRay = calculateRay();
if (intersectionInRange(0, RAY_RANGE, currentRay)) {
currentTerrainPoint = binarySearch(0, 0, RAY_RANGE, currentRay);
} else {
currentTerrainPoint = null;
}
}
private Vector3f calculateRay()
{
float mouseX = (float) input.getMouseDx();
float mouseY = (float) input.getMouseDy();
Vector2f deviceCoords = getNormalizedDeviceCoordinates(mouseX, mouseY);
//System.out.println(deviceCoords.x+", "+deviceCoords.y);
Vector4f clipCoords = new Vector4f(deviceCoords.x, deviceCoords.y, -1f, 1f);
Vector4f eyeCoords = toEyeCoords(clipCoords);
Vector3f worldRay = toWorldCoords(eyeCoords);
return worldRay;
}
private Vector3f toWorldCoords(Vector4f eyeCoords)
{
Matrix4f invertedView = viewMatrix.invert();
Vector4f rayWorld = invertedView.transform(eyeCoords);
Vector3f mouseRay = new Vector3f(rayWorld.x, rayWorld.y, rayWorld.z);
mouseRay.normalize();
return mouseRay;
}
private Vector4f toEyeCoords(Vector4f clipCoords)
{
Matrix4f invertedProjection = projectionMatrix.invert();
Vector4f eyeCoords = invertedProjection.transform(clipCoords);
return new Vector4f(eyeCoords.x, eyeCoords.y, -1f, 0f);
}
private Vector2f getNormalizedDeviceCoordinates(float mouseX, float mouseY)
{
float x = (2f * mouseX) / Constants.DISPLAY_WIDTH - 1f;
float y = (2f * mouseY) / Constants.DISPLAY_HEIGHT - 1f;
return new Vector2f(x, -y);
}
private Vector3f getPointOnRay(Vector3f ray, float distance) {
//Vector3f camPos = new Vector3f(camera.getPosX(), camera.getPosY(), camera.getPosZ());
Vector3f start = new Vector3f(camera.getPosX(), camera.getPosY(), camera.getPosZ());
Vector3f scaledRay = new Vector3f(ray.x * distance, ray.y * distance, ray.z * distance);
return start.add(scaledRay);
}
private Vector3f binarySearch(int count, float start, float finish, Vector3f ray) {
float half = start + ((finish - start) / 2f);
if (count >= RECURSION_COUNT) {
Vector3f endPoint = getPointOnRay(ray, half);
Terrain terrain = getTerrain(endPoint.x, endPoint.z);
if (terrain != null) {
return endPoint;
} else {
return null;
}
}
if (intersectionInRange(start, half, ray)) {
return binarySearch(count + 1, start, half, ray);
} else {
return binarySearch(count + 1, half, finish, ray);
}
}
private boolean intersectionInRange(float start, float finish, Vector3f ray) {
Vector3f startPoint = getPointOnRay(ray, start);
Vector3f endPoint = getPointOnRay(ray, finish);
if (!isUnderGround(startPoint) && isUnderGround(endPoint)) {
return true;
} else {
return false;
}
}
private boolean isUnderGround(Vector3f testPoint) {
Terrain terrain = getTerrain(testPoint.x, testPoint.z);
float height = 0;
if (terrain != null) {
height = terrain.getTerrainHeight(testPoint.x, testPoint.z);
}
if (testPoint.y < height) {
return true;
} else {
return false;
}
}
private Terrain getTerrain(float worldX, float worldZ) {
return terrain;
}
public Vector3f getCurrentTerrainPoint() {
return currentTerrainPoint;
}
public Vector3f getCurrentRay() {
return currentRay;
}
}
MathUtils class with view and projection matrices:
public class MathUtils {
public static float baryCentric(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f pos) {
float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
float l3 = 1.0f - l1 - l2;
return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}
public static Matrix4f createTransformationMatrix(Vector2f translation, Vector2f scale) {
Matrix4f matrix = new Matrix4f();
matrix.identity();
matrix.translate(translation.x,translation.y,0f);
matrix.scale(scale.x,scale.y,1f);
return matrix;
}
public static Matrix4f createTransformationMatrix(Vector3f translation, float rx, float ry, float rz, float scale) {
Matrix4f transformationMatrix = new Matrix4f();
transformationMatrix.identity();
transformationMatrix.translate(translation);
transformationMatrix.rotate((float) Math.toRadians(rx), 1,0,0);
transformationMatrix.rotate((float) Math.toRadians(ry), 0,1,0);
transformationMatrix.rotate((float) Math.toRadians(rz), 0,0,1);
transformationMatrix.scale(scale);
return transformationMatrix;
}
public static Matrix4f createViewMatrix(Camera camera) {
Matrix4f viewMatrix = new Matrix4f();
viewMatrix.identity();
viewMatrix = viewMatrix.rotate((float) Math.toRadians(camera.getPitch()), 1,0,0);//((float) Math.toRadians(camera.getPitch()), new Vector3f(1, 0, 0), viewMatrix);
viewMatrix = viewMatrix.rotate((float) Math.toRadians(camera.getYaw()),0, 1, 0);
Vector3f cameraPos = new Vector3f(camera.getPosX(), camera.getPosY(), camera.getPosZ());
Vector3f negativeCameraPos = new Vector3f(-cameraPos.x, -cameraPos.y, -cameraPos.z);
viewMatrix = viewMatrix.translate(negativeCameraPos);
return viewMatrix;
}
public static Matrix4f createProjectionMatrix() {
Matrix4f projectionMatrix = new Matrix4f();
float aspectRatio = (float) Constants.DISPLAY_WIDTH / (float) Constants.DISPLAY_HEIGHT;
float fov = Constants.FOV;
float near = Constants.NEAR_PLANE;
float far = Constants.FAR_PLANE;
projectionMatrix = projectionMatrix.perspective((float) java.lang.Math.toRadians(fov), aspectRatio, near, far);
return projectionMatrix;
}
A coordinate in view space is a Cartesian coordinates with 3 components x, y and z. The projection matrix transforms from view space to clip space. Clip space coordinates are Homogeneous coordinates with 4 components x, y, z and w.
Clip space coordinates can be transformed to normalized device coordinates by a Perspective divide.
This means the x, y and z component is divided by w component.
If you want to transform from normalized device space to view space, then you've to do the inverse operation. this means you've to transform by the inverse projection matrix and to divide the x, y and z component of the result by the w component of the result.
private Vector4f toEyeCoords(Vector4f ndcCoords)
{
Matrix4f invertedProjection = projectionMatrix.invert(new Matrix4f());
Vector4f eyeCoords = invertedProjection.transform(clipCoords);
return new Vector4f(eyeCoords.x/eyeCoords.w, eyeCoords.y/eyeCoords.w, eyeCoords.z/eyeCoords.w, 0.0f);
}
I have recently created 2D height map grid which generates 3D terrain mesh for my world With the ability to add hills/bumps with mouse click events during runtime. My Problem is that every time i add to the height of the vertices i update the whole terrain's normal and position vbos(very not efficient). what is the way to
change specific part of vbo?
I have heared that glBufferSubData is the way, but How can i change only the Y value? (the vbo is x,y,z,x,y,z...)
and get the changed verticies in order for glBufferSubData?
Terrain class:
public class Terrain {
public static final int SIZE = 500;
//VAO, vertexCount, VBOS
private RawModel model;
//textures for the terrain
private terrainTexturePack texturePack;
Loader loader;
private static int VERTEX_COUNT =128;
float[] Vertices;
float[] Normals;
float[] TextureCoords;
int[] Indices;
private float[][] heights;
public Terrain(Loader loader, terrainTexturePack texturePack) {
this.texturePack = texturePack;
this.loader = loader;
this.model = generateTerrain(loader);
}
public RawModel getModel() {
return model;
}
public terrainTexturePack getTexturePack() {
return texturePack;
}
//player collision detection witn the terrain
public Vector3f getXYZOfTerrain(float worldX, float worldZ) {
float gridSquareSize = SIZE / ((float) heights.length - 1);
int gridX = (int) Math.floor(worldX / gridSquareSize);
int gridZ = (int) Math.floor(worldZ / gridSquareSize);
if(gridX >= heights.length - 1 || gridZ >= heights.length - 1 || gridX < 0 || gridZ < 0) {
return null;
}
float xCoord = (worldX % gridSquareSize)/gridSquareSize;
float zCoord = (worldZ % gridSquareSize)/gridSquareSize;
float yCoord;
if (xCoord <= (1-zCoord)) {
yCoord = Maths.barryCentric(new Vector3f(0, heights[gridX][gridZ], 0), new Vector3f(1,
heights[gridX + 1][gridZ], 0), new Vector3f(0,
heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
} else {
yCoord = Maths.barryCentric(new Vector3f(1, heights[gridX + 1][gridZ], 0), new Vector3f(1,
heights[gridX + 1][gridZ + 1], 1), new Vector3f(0,
heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
}
return new Vector3f(gridX, yCoord, gridZ);
}
//GENERATE THE TERRAIN
private RawModel generateTerrain(Loader loader) {
int pointer = 0;
int count = VERTEX_COUNT * VERTEX_COUNT;
heights = new float[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;
float height = 0f;
vertices[vertexPointer * 3 + 1] = height;
heights[j][i] = height;
vertices[vertexPointer * 3 + 2] = (float) i / ((float) VERTEX_COUNT - 1) * SIZE;
Vector3f normal =new Vector3f(0, 1, 0);// calculateNormal(j, i, noise);
normals[vertexPointer * 3] = normal.x;
normals[vertexPointer * 3 + 1] = normal.y;
normals[vertexPointer * 3 + 2] = normal.z;
textureCoords[vertexPointer * 2] = (float) j / ((float) VERTEX_COUNT - 1);
textureCoords[vertexPointer * 2 + 1] = (float) i / ((float) VERTEX_COUNT - 1);
vertexPointer++;
if(i < VERTEX_COUNT - 1 && j < VERTEX_COUNT - 1){
int topLeft = (i * VERTEX_COUNT) + j;
int topRight = topLeft + 1;
int bottomLeft = ((i + 1) * VERTEX_COUNT) + j;
int bottomRight = bottomLeft + 1;
indices[pointer++] = topLeft;
indices[pointer++] = bottomLeft;
indices[pointer++] = topRight;
indices[pointer++] = topRight;
indices[pointer++] = bottomLeft;
indices[pointer++] = bottomRight;
}
}
}
Vertices = vertices;
TextureCoords = textureCoords;
Normals = normals;
Indices = indices;
return loader.loadToVAO(vertices, textureCoords, normals, indices);
}
//Calculate normal
private Vector3f calculateNormal(int x, int z) {
float heightL = Vertices[((( (z) *VERTEX_COUNT)+ (x-1) )*3)+1];
float heightR = Vertices[((( (z) *VERTEX_COUNT)+ (x+1) )*3)+1];
float heightD = Vertices[((( (z-1) *VERTEX_COUNT)+ (x) )*3)+1];
float heightU = Vertices[((( (z+1) *VERTEX_COUNT)+ (x) )*3)+1];
Vector3f normal = new Vector3f(heightL - heightR, 2f, heightD - heightU);
normal.normalise();
return normal;
}
//create mountain where the mouse clicked
//Vertices[(((y*VERTEX_COUNT)+x)*3)+1] = one Vertex in 2d grid
public void createHill(int x0, int y0){
float h = 0.06f;
int xs=VERTEX_COUNT;
int ys=VERTEX_COUNT;
float maxHeight =Vertices[(((y0*xs)+x0)*3)+1]+h;
float r = (9*maxHeight)/30;
//Loop the vertices
for(int y=(int) (y0-r);y<=y0+r;y++)
for(int x=(int) (x0-r);x<=x0+r;x++){
double circule = Math.sqrt((x-x0)*(x-x0)+(y0-y)*(y0-y));
if (circule <= r)
if ((x>=1)&&(x<xs-1))
if ((y>=1)&&(y<ys-1)){
Vertices[(((y*xs)+x)*3)+1] = Maths.hillsHeight(x0, x, y0, y,(maxHeight), r);
Vector3f normal = calculateNormal(x,y);
Normals[((((y*xs)+x))) * 3] = normal.x;
Normals[((((y*xs)+x))) * 3 + 1] = normal.y;
Normals[((((y*xs)+x))) * 3 + 2] = normal.z;
}
}
//change the whole VBO's not effective
//Note: i know that i dont need to update textures and indices
this.model=loader.loadToVAO(Vertices, TextureCoords, Normals, Indices);
}
}
Raw model class(vbo and vao holder):
//Store the VAOS and VBOS
public class RawModel {
private int vaoID;
private int vertexCount;
private int positionVbo;
private int normalVbo;
private int textureVbo;
public RawModel(int vaoID, int vertexCount, int positionVbo, int normalVbo, int textureVbo) {
this.vaoID = vaoID;
this.vertexCount = vertexCount;
this.positionVbo = positionVbo;
this.normalVbo = normalVbo;
this.textureVbo = textureVbo;
}
public RawModel(int vaoID, int vertexCount) {
this.vaoID = vaoID;
this.vertexCount = vertexCount;
}
public int getVaoID() {
return vaoID;
}
public int getVertexCount() {
return vertexCount;
}
public int getPositionVbo() {
return positionVbo;
}
public int getTextureVbo() {
return textureVbo;
}
public int getNormalVbo() {
return normalVbo;
}
}
loader class:
public class Loader {
//For clean up
private List<Integer> vaos = new ArrayList<Integer>();
private List<Integer> vbos = new ArrayList<Integer>();
private List<Integer> textures = new ArrayList<Integer>();
//Load mesh into VAO
public RawModel loadToVAO(float[] positions,float[] textureCoords,float[] normals,int[] indices){
int vaoID = createVAO();
bindIndicesBuffer(indices);
int positionvbo = storeDataInAttributeList(0,3,positions);
int textureVbo = storeDataInAttributeList(1,2,textureCoords);
int normalsnvbo = storeDataInAttributeList(2,3,normals);
unbindVAO();
return new RawModel(vaoID,indices.length, positionvbo, textureVbo, normalsnvbo);
}
//Load texture
public int loadTexture(String fileName) {
Texture texture = null;
try {
texture = TextureLoader.getTexture("PNG",
new FileInputStream("res/textures/" + fileName + ".png"));
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_LINEAR_MIPMAP_LINEAR);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, -2);
if(GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){
float amount = Math.min(4f,
GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT));
GL11.glTexParameterf(GL11.GL_TEXTURE_2D,
EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, amount);
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Tried to load texture " + fileName + ".png , didn't work");
System.exit(-1);
}
textures.add(texture.getTextureID());
return texture.getTextureID();
}
//Clean up
public void cleanUp(){
for(int vao:vaos){
GL30.glDeleteVertexArrays(vao);
}
for(int vbo:vbos){
GL15.glDeleteBuffers(vbo);
}
for(int texture:textures){
GL11.glDeleteTextures(texture);
}
}
//Creates vao
private int createVAO(){
int vaoID = GL30.glGenVertexArrays();
vaos.add(vaoID);
GL30.glBindVertexArray(vaoID);
return vaoID;
}
//Store data in vbo
private int storeDataInAttributeList(int attributeNumber, int coordinateSize,float[] data){
int vboID = GL15.glGenBuffers();
vbos.add(vboID);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
FloatBuffer buffer = storeDataInFloatBuffer(data);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(attributeNumber,coordinateSize,GL11.GL_FLOAT,false,0,0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
return vboID;
}
private void unbindVAO(){
GL30.glBindVertexArray(0);
}
//Bind indices buffer
private void bindIndicesBuffer(int[] indices){
int vboID = GL15.glGenBuffers();
vbos.add(vboID);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboID);
IntBuffer buffer = storeDataInIntBuffer(indices);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
}
//Store in int buffer
private IntBuffer storeDataInIntBuffer(int[] data){
IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
//Store in float buffer
private FloatBuffer storeDataInFloatBuffer(float[] data){
FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
//Load skyBox textures
public int loadCubeMap(String[] textureFiles){
int texID = GL11.glGenTextures();
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, texID);
for(int i = 0; i < textureFiles.length; i++){
TextureData data = decodeTextureFile("res/textures/"+ textureFiles[i] + ".png");
GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL11.GL_RGBA, data.getWidth(), data.getHeight(), 0,
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data.getBuffer());
}
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
textures.add(texID);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
return texID;
}
private TextureData decodeTextureFile(String fileName) {
int width = 0;
int height = 0;
ByteBuffer buffer = null;
try {
FileInputStream in = new FileInputStream(fileName);
PNGDecoder decoder = new PNGDecoder(in);
width = decoder.getWidth();
height = decoder.getHeight();
buffer = ByteBuffer.allocateDirect(4 * width * height);
decoder.decode(buffer, width * 4, Format.RGBA);
buffer.flip();
in.close();
} catch (Exception e) {
e.printStackTrace();
System.err.println("Tried to load texture " + fileName + ", didn't work");
System.exit(-1);
}
return new TextureData(buffer, width, height);
}
//Load textures for GUI
public RawModel loadToVAO(float[] positions, int dimensions) {
int vaoID = createVAO();
this.storeDataInAttributeList(0, dimensions, positions);
unbindVAO();
return new RawModel(vaoID, positions.length / dimensions);
}
}
Solved Thank to Reto Koradi
public void changeVbo(int position, float[] data, int VboId){
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VboId);
FloatBuffer ArrayData = storeDataInFloatBuffer(data);
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER,position * 4, ArrayData);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
The easiest and likely most efficient way is to store the heights (y-values) in a separate VBO, and specify them as a separate vertex attribute.
Then, in your vertex shader code, you can simply reassemble the position from the separate attributes. You might have something like this in your shader code now:
in vec3 pos;
This changes to:
in vec3 posXZ;
in float posY;
...
vec3 pos = vec3(posXZ.x, posY, posXZ.y);
Using a separate VBO for data that changes frequently also allows you to specify the allocation flags accordingly. You can use GL_DYNAMIC_DRAW for the data that changes frequently, GL_STATIC_DRAW for the rest.
Another option would be to use glMapBuffer(). This gives you a CPU pointer to the buffer content, allowing you to modify only the data that you actually want to change. However, you'll have to be careful that you don't introduce undesirable synchronization between CPU and GPU. The glMapBuffer() call might block until the GPU finished all rendering calls using the previous content of the buffer. One common technique is to use multiple copies of the data in a set of buffers, and cycle through them, to minimize synchronization. But if the amount of data is large, that will obviously cause memory usage to increase dramatically.
In your use case, I suspect that you'll have to update the normals as well, since they depend on the height values.
I am trying to make a 2D game with opengl.I made the the class which draw meshes.And i made my own Matrix4f class.I have experience with 3D opengl.
When i pass my matrix4f to the shader it doesn't draw.But when i remove it from multiplying with the position the triangle appears on the screen.I tried my Matrix4f from my 3D Game Engine but it doesn't work as well.
Matrix4f class
package com.game.main.maths;
import java.nio.FloatBuffer;
import com.game.main.util.Util;
public class Matrix4f {
private float[][] m;
public Matrix4f() {
m = new float[4][4];
initIdentity();
}
public Matrix4f initIdentity() {
for (int x = 0; x < 4; x++)
for (int y = 0; y < 4; y++) {
if (x == y)
m[x][y] = 1.0f;
else
m[x][y] = 0;
}
return this;
}
public Matrix4f translate(float x, float y, float z) {
initIdentity();
m[0][3] = x;
m[1][3] = y;
m[2][3] = z;
return this;
}
public Matrix4f translate(Vector3f pos) {
initIdentity();
m[0][3] = pos.getX();
m[1][3] = pos.getY();
m[2][3] = pos.getZ();
return this;
}
public Matrix4f rotate(float angle) {
initIdentity();
float rad = (float) Math.toRadians(angle);
float cos = (float) Math.cos(rad);
float sin = (float) Math.sin(rad);
m[0][0] = cos;
m[1][0] = sin;
m[0][1] = -sin;
m[1][1] = cos;
return this;
}
public Matrix4f initOrthographic(float left, float right, float bottom,
float top, float near, float far) {
initIdentity();
m[0][0] = 2.0f / (right - left);
m[1][1] = 2.0f / (top - bottom);
m[2][2] = 2.0f / (near - far);
m[0][3] = (left + right) / (left - right);
m[1][3] = (bottom + top) / (bottom - top);
m[2][3] = (near + far) / (far - near);
return this;
}
public Matrix4f mul(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 index = 0; index < 4; index++)
sum += m[index][y] * matrix.get(x, index);
result.set(x, y, sum);
}
}
return result;
}
public float[][] getM() {
return m;
}
public float get(int x, int y) {
return m[x][y];
}
public void setM(float[][] m) {
this.m = m;
}
public void set(int x, int y, float value) {
m[x][y] = value;
}
public FloatBuffer toFloatBuffer() {
return Util.createFlippedBufferBuffer(this);
}
}
Shader class function
public void setUniform(String uniformName, Matrix4f matrix){
int uniformLocation = getUniformLocation(uniformName);
glUniformMatrix4(uniformLocation, false, matrix.toFloatBuffer());
}
Util class
package com.game.main.util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import com.game.main.graphics.Vertex;
import com.game.main.maths.Matrix4f;
public class Util {
public static FloatBuffer createFlippedBuffer(float[] data){
FloatBuffer buffer = ByteBuffer.allocateDirect(data.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
buffer.put(data).flip();
return buffer;
}
public static IntBuffer createFlippedBuffer(int[] data){
IntBuffer buffer = ByteBuffer.allocateDirect(data.length * 4).order(ByteOrder.nativeOrder()).asIntBuffer();
buffer.put(data).flip();
return buffer;
}
public static IntBuffer createIntBuffer(int size){
IntBuffer buffer = ByteBuffer.allocateDirect(size * 4).order(ByteOrder.nativeOrder()).asIntBuffer();
return buffer;
}
public static FloatBuffer createFloatBuffer(int size){
FloatBuffer buffer = ByteBuffer.allocateDirect(size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
return buffer;
}
public static FloatBuffer createFlippedBufferBuffer(Matrix4f matrix){
FloatBuffer buffer = createFloatBuffer(4 * 4);
for(int x = 0; x < 4; x++)
for(int y = 0; y < 4; y++)
buffer.put(matrix.get(x, y));
buffer.flip();
return buffer;
}
public static FloatBuffer createFlippedBuffer(Vertex[] vertecies){
FloatBuffer buffer = createFloatBuffer(vertecies.length * Vertex.SIZE);
for(int i = 0; i < vertecies.length; i++){
buffer.put(vertecies[i].getPos().getX());
buffer.put(vertecies[i].getPos().getY());
buffer.put(vertecies[i].getPos().getZ());
buffer.put(vertecies[i].getTexCoord().getX());
buffer.put(vertecies[i].getTexCoord().getY());
}
buffer.flip();
return buffer;
}
}
Initializing the matrix uniform
Matrix4f matrix = new Matrix4f().initOrthographic(0, 800, 0, 600, -1.0f, 1.0f);
Shader.Object.setUniform("mat", matrix);
Vertex Shader
#version 330
layout(location = 0) in vec3 pos;
uniform mat4 mat;
void main(){
gl_Position = mat * vec4(pos, 1.0f);
}
Mesh class
package com.game.main.graphics;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import com.game.main.util.Util;
public class Mesh {
private int vbo, ibo, size;
public Mesh(Vertex[] vertecies, int[] indices){
vbo = glGenBuffers();
ibo = glGenBuffers();
size = indices.length;
createMesh(vertecies, indices);
}
private void createMesh(Vertex[] vertecies, int[] indices){
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, Util.createFlippedBuffer(vertecies), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * 4, 0);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * 4, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, Util.createFlippedBuffer(indices), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
public void bind(){
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
}
public void unbind(){
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
public void drawWithBinding()
{
bind();
glDrawElements(GL_TRIANGLES, size, GL_UNSIGNED_INT, 0);
unbind();
}
public void draw(){
glDrawElements(GL_TRIANGLES, size, GL_UNSIGNED_INT, 0);
}
public void destroy(){
glDeleteBuffers(ibo);
glDeleteBuffers(vbo);
}
}
Vertex class
package com.game.main.graphics;
import com.game.main.maths.Vector2f;
import com.game.main.maths.Vector3f;
public class Vertex {
private Vector3f pos = new Vector3f();
private Vector2f texCoord = new Vector2f();
public static final int SIZE = 5;
public Vertex(Vector3f pos){
this(pos, new Vector2f());
}
public Vertex(Vector3f pos, Vector2f texCoord){
this.pos = pos;
this.texCoord = texCoord;
}
public Vector3f getPos() {
return pos;
}
public void setPos(Vector3f pos) {
this.pos = pos;
}
public Vector2f getTexCoord() {
return texCoord;
}
public void setTexCoord(Vector2f texCoord) {
this.texCoord = texCoord;
}
}
transpose code
public Matrix4f transpose() {
float m00 = get(0, 0);
float m01 = get(1, 0);
float m02 = get(2, 0);
float m03 = get(3, 0);
float m10 = get(0, 1);
float m11 = get(1, 1);
float m12 = get(2, 1);
float m13 = get(3, 1);
float m20 = get(0, 2);
float m21 = get(1, 2);
float m22 = get(2, 2);
float m23 = get(3, 2);
float m30 = get(0, 3);
float m31 = get(1, 3);
float m32 = get(2, 3);
float m33 = get(3, 3);
set(0, 0, m00);
set(0, 1, m01);
set(0, 2, m02);
set(0, 3, m03);
set(1, 0, m10);
set(1, 1, m11);
set(1, 2, m12);
set(1, 3, m13);
set(2, 0, m20);
set(2, 1, m21);
set(2, 2, m22);
set(2, 3, m23);
set(3, 0, m30);
set(3, 1, m31);
set(3, 2, m32);
set(3, 3, m33);
return this;
}
Game class
package com.game.main;
import static org.lwjgl.opengl.GL11.*;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import com.game.main.graphics.Mesh;
import com.game.main.graphics.Shader;
import com.game.main.graphics.Vertex;
import com.game.main.maths.Matrix4f;
import com.game.main.maths.Vector3f;
public class Game{
private boolean running = false;
private Mesh mesh;
public Game(int width, int height){
try {
Display.setDisplayMode(new DisplayMode(800, 600));
Display.setTitle("2D Game");
Display.create();
} catch (LWJGLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Matrix4f matrix = new Matrix4f().initOrthographic(-1, 1, -1, 1, -1.0f, 1.0f);
Shader.Object.setUniform("mat", matrix);
Vertex[] vertecies = new Vertex[]{
new Vertex(new Vector3f(0.5f, 0.5f, 0)),
new Vertex(new Vector3f(0.5f, 0,0)),
new Vertex(new Vector3f(0, 0.5f,0)),
new Vertex(new Vector3f(0.5f, 0,0))
};
/*float[] vertecies = new float[]{
-10f, -10f * 9.0f / 16.0f, 1,
-10f, 10f * 9.0f / 16.0f,1,
0, 10f * 9.0f / 1.0f,1,
0, -10f * 9.0f / 6.0f,1
};*/
int[] ind = new int[]{
0, 1, 2,
2, 3, 0
};
mesh = new Mesh(vertecies, ind);
}
private void init(){
glEnable(GL_DEPTH_TEST);
}
public void start(){
if(running)
return;
running = true;
gameLoop();
}
public void stop(){
if(!running)
return;
running = false;
}
private void gameLoop(){
init();
long timer = System.currentTimeMillis();
long lastTime = System.nanoTime();
double delta = 0;
int frames = 0, updates = 0;
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / (1000000000.0 / 60.0);
lastTime = now;
while(delta >= 1){
if(Display.isCloseRequested())
stop();
update();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer >= 1000){
timer += 1000;
Display.setTitle("Frames: "+frames+" Updates: "+updates);
frames = updates = 0;
}
}
destroy();
}
private void update(){
}
private void render(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Shader.Object.bind();
mesh.drawWithBinding();
Shader.Object.unbind();
Display.update();
}
private void destroy(){
mesh.destroy();
Display.destroy();
}
}
It seems the problem is in setting the matrix uniform. OpenGL is a state machine and doesn't remember uniform values. More importantly, glUniformMatrix4 must be called while the shader is in use.
Currently, the matrix is set only at initialization (in the Game constructor), when it needs to be in render().
Shader.Object.bind(); //glUseProgram(programHandle)
Shader.Object.setUniform(...) //glUniformMatrix4
mesh.drawWithBinding(); //glDrawElements
Shader.Object.unbind(); //glUseProgram(0), but not necessary unless you want fixed function rendering
With mat not being set it'd probably be a zero matrix that was stopping things from drawing.
In addition to the above, there are two other potential issues:
Take a look in the bottom left of your screen and see if that pixel is on. I suspect everything's working, just really small. If the following works:
gl_Position = vec4(pos, 1.0f);
which is essentially a multiply by the identity matrix and nearly ortho(-1,1,-1,1,-1,1) (except the ortho call gives mat[2][2]=-1), then I'm guessing your mesh is around the size of a unit cube.
Introducing mat = initOrthographic(0, 800, 0, 600, -1.0f, 1.0f) looks like it's set up for a scene where vertices are given in pixels. The origin will be bottom left and if the mesh fits in a unit cube will draw to at most one pixel.
See #Jerem's answer about row/column major matrices.
It looks like your matrix is row major. You need to transpose it before sending it to the shader.
Note that there is a boolean parameter called transpose in glUniformMatrix4f but you can't use it in opengl es - the doc says it has to be false - you have to transpose it yourself.
I'm in major need for help. I am working on a 2d game in libgdx. All is working well, and today I decided to make a small 2d particle system to make fireworks. I remembered I had recently accomplished this task using shaders in 3d in the book OpenGL ES 2.0 for Android: A Quick Start Guide. So I copied most of the code and started modifying some things so it would work with Libgdx and in 2d. How this particle editor works is there is one big float array that holds 8 things per particle: 2 positions, x and y; 3 colors, rgb; 2 directions, x and y; 1 start time. Now this repeats for every particle allowed in the array. I set the max particles to 10,000, so 80,000 floats in this array, meaning 320,000 bytes. Anyways, now I have all of these attributes. Now I put these attributes into the shader program. The shader program uses these to move the particles by the direction, fade the color by time, and apply gravity. So anyways, it worked uses pure OpenGL, but I wanted to use the LibGDX framework. I tried to keep as much as the old code as I could. I thought what I did would work, but it won't show anything. Here is the code...
ParticleFirework.java:
public class ParticleFirework {
private final Vector2 position;
private final Vector2 direction;
private final int color;
private final float angleVariance;
private final float speedVariance;
private final Random random = new Random();
private float[] rotationMatrix = new float[16];
private float[] directionVector = new float[4];
private float[] resultVector = new float[4];
public ParticleFirework(Vector2 position, Vector2 direction, int color,
float angleVarianceInDegrees, float speedVariance) {
this.position = position;
this.direction = direction;
this.color = color;
this.angleVariance = angleVarianceInDegrees;
this.speedVariance = speedVariance;
directionVector[0] = direction.x;
directionVector[1] = direction.y;
directionVector[2] = 0;
}
public void addParticles(ParticleSystem particleSystem, float currentTime,
int count) {
for (int i = 0; i < count; i++) {
setRotateEulerM(rotationMatrix, 0, (random.nextFloat() - 0.5f)
* angleVariance, (random.nextFloat() - 0.5f)
* angleVariance, (random.nextFloat() - 0.5f)
* angleVariance);
multiplyMV(resultVector, 0, rotationMatrix, 0, directionVector, 0);
float speedAdjustment = 1f + random.nextFloat() * speedVariance;
Vector2 thisDirection = new Vector2(resultVector[0]
* speedAdjustment, resultVector[1] * speedAdjustment);
particleSystem.addParticle(position, color, thisDirection,
currentTime);
}
}
}
ParticleSystem.java:
public class ParticleSystem {
private static final int PARTICLE_POSITION_COUNT = 2;
private static final int PARTICLE_COLOR_COUNT = 3;
private static final int PARTICLE_DIRECTION_COUNT = 2;
private static final int PARTICLE_START_TIME_COUNT = 1;
private static final int BYTES_IN_A_FLOAT = 4;
private static final int TOTAL_COUNT = PARTICLE_POSITION_COUNT
+ PARTICLE_COLOR_COUNT + PARTICLE_DIRECTION_COUNT
+ PARTICLE_START_TIME_COUNT;
private static final int STRIDE = TOTAL_COUNT * BYTES_IN_A_FLOAT;
private final float[] particles;
private final int maxParticleCount;
private int currentParticleCount;
private int nextParticle;
private FloatBuffer particleFloatBuffer;
public ParticleSystem(int maxParticleCount) {
particles = new float[maxParticleCount * TOTAL_COUNT];
this.maxParticleCount = maxParticleCount;
particleFloatBuffer = ByteBuffer
.allocateDirect(particles.length * BYTES_IN_A_FLOAT)
.order(ByteOrder.nativeOrder()).asFloatBuffer().put(particles);
}
public void addParticle(Vector2 position, int color, Vector2 direction,
float particleStartTime) {
final int particleOffset = nextParticle * TOTAL_COUNT;
int currentOffset = particleOffset;
nextParticle++;
if (currentParticleCount < maxParticleCount) {
currentParticleCount++;
}
if (nextParticle == maxParticleCount) {
nextParticle = 0;
}
particles[currentOffset++] = position.x;
particles[currentOffset++] = position.y;
particles[currentOffset++] = Color.red(color) / 255f;
particles[currentOffset++] = Color.green(color) / 255f;
particles[currentOffset++] = Color.blue(color) / 255f;
particles[currentOffset++] = direction.x;
particles[currentOffset++] = direction.y;
particles[currentOffset++] = particleStartTime;
particleFloatBuffer.position(particleOffset);
particleFloatBuffer.put(particles, particleOffset, TOTAL_COUNT);
particleFloatBuffer.position(0);
}
public void bindData(ShaderProgram particleProgram) {
int dataOffset = 0;
particleProgram.setVertexAttribute("a_Position",
PARTICLE_POSITION_COUNT, GL20.GL_FLOAT, false, STRIDE,
dataOffset);
dataOffset += PARTICLE_POSITION_COUNT;
particleProgram.setVertexAttribute("a_Color",
PARTICLE_COLOR_COUNT, GL20.GL_FLOAT, false, STRIDE,
dataOffset);
dataOffset += PARTICLE_COLOR_COUNT;
particleProgram.setVertexAttribute("a_DirectionVector",
PARTICLE_DIRECTION_COUNT, GL20.GL_FLOAT, false, STRIDE,
dataOffset);
dataOffset += PARTICLE_DIRECTION_COUNT;
particleProgram.setVertexAttribute("a_ParticleStartTime",
PARTICLE_START_TIME_COUNT, GL20.GL_FLOAT, false, STRIDE,
dataOffset);
}
public void draw() {
glDrawArrays(GL_POINTS, 0, currentParticleCount);
}
}
ParticleGame.java:
public class ParticlesGame extends ApplicationAdapter {
SpriteBatch batch;
ShaderProgram particleProgram;
OrthographicCamera camera;
Texture img;
private ParticleSystem particleSystem;
private ParticleFirework firework;
private long globalStartTime;
#Override
public void create() {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
globalStartTime = System.nanoTime();
final Vector2 particleDirection = new Vector2(0f, 0.5f);
final float angleVarianceInDegrees = 5f;
final float speedVariance = 1f;
particleProgram = new ShaderProgram("shaders/particle_shader.vs",
"shaders/particle_shader.fs");
particleSystem = new ParticleSystem(10000);
firework = new ParticleFirework(new Vector2(0f, 0f), particleDirection,
Color.rgb(255, 50, 5), angleVarianceInDegrees, speedVariance);
}
#Override
public void render() {
float currentTime = (System.nanoTime() - globalStartTime) / 1000000000f;
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
firework.addParticles(particleSystem, currentTime, 5);
particleProgram.begin();
FloatBuffer matrixBuffer = ByteBuffer
.allocateDirect(camera.combined.getValues().length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(camera.combined.getValues());
/** Set the uniforms **/
particleProgram.setUniformMatrix4fv("u_matrix", matrixBuffer, 1, false);
particleProgram.setUniform1fv("u_Time", new float[] {currentTime}, 0, 1);
/** Bind data **/
particleSystem.bindData(particleProgram);
particleSystem.draw();
}
}
particle_shader.vs
uniform mat4 u_Matrix;
uniform float u_Time;
attribute vec3 a_Position;
attribute vec3 a_Color;
attribute vec3 a_DirectionVector;
attribute float a_ParticleStartTime;
varying vec3 v_Color;
varying float v_ElapsedTime;
void main()
{
v_Color = a_Color;
v_ElapsedTime = u_Time - a_ParticleStartTime;
float gravityFactor = v_ElapsedTime * v_ElapsedTime / 8.0;
vec3 currentPosition = a_Position + (a_DirectionVector * v_ElapsedTime);
currentPosition.y -= gravityFactor;
gl_Position = u_Matrix * vec4(currentPosition, 1.0);
gl_PointSize = 25.0;
}
I
I am trying to implement object picking based on touch coordinates via an intersecting ray test.
I am having trouble finding information on converting the touch coordinates to the coordinate system used in the world in order to construct this ray.
My understanding so far is that the matrix that is applied to each vertex in the scene is:
projectionMatrix * viewMatrix * modelMatrix
Here is my process for reversing that process in a an attempt to find the ray's endpoint in the scene as well as my drawing loop in case I'm simply applying the different matrices incorrectly:
public float[] getMouseRayProjection(float touchX, float touchY, float windowWidth, float windowHeight, float[] modelView, float[] projection)
{
float[] rayDirection = new float[4];
float normalizedX = 2 * touchX/windowWidth - 1;
float normalizedY = 1 - 2*touchY/windowHeight;
float[] unviewMatrix = new float[16];
float[] viewMatrix = new float[16];
Matrix.multiplyMM(viewMatrix, 0, projection, 0, modelView, 0);
Matrix.invertM(unviewMatrix, 0, viewMatrix, 0);
float[] nearPoint = multiplyMat4ByVec4(projection, new float[]{normalizedX, normalizedY, 0, 1});
float[] modelviewInverse = new float[16];
Matrix.invertM(modelviewInverse, 0, modelView, 0);
float[] cameraPos = new float[4];
cameraPos[0] = modelviewInverse[12];
cameraPos[1] = modelviewInverse[13];
cameraPos[2] = modelviewInverse[14];
cameraPos[3] = modelviewInverse[15];
rayDirection[0] = nearPoint[0] - cameraPos[0];
rayDirection[1] = nearPoint[1] - cameraPos[1];
rayDirection[2] = nearPoint[2] - cameraPos[2];
rayDirection[3] = nearPoint[3] - cameraPos[3];
return rayDirection;
}
public float[] multiplyMat4ByVec4(float[] matrix4, float[] vector4)
{
float[] returnMatrix = new float[4];
returnMatrix[0] = (matrix4[0] * vector4[0]) + (matrix4[1] * vector4[1]) + (matrix4[2] * vector4[2]) + (matrix4[3] * vector4[3]);
returnMatrix[1] = (matrix4[4] * vector4[0]) + (matrix4[5] * vector4[1]) + (matrix4[6] * vector4[2]) + (matrix4[7] * vector4[3]);
returnMatrix[2] = (matrix4[8] * vector4[0]) + (matrix4[9] * vector4[1]) + (matrix4[10] * vector4[2]) + (matrix4[11] * vector4[3]);
returnMatrix[3] = (matrix4[12] * vector4[0]) + (matrix4[13] * vector4[1]) + (matrix4[14] * vector4[2]) + (matrix4[15] * vector4[3]);
return returnMatrix;
}
#Override
public void onDrawFrame(GL10 gl10) {
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
long time = SystemClock.uptimeMillis() % 10000L;
float angleInDegrees = (360.0f / 10000.0f) * ((int) time);
GLES20.glViewport(0, 0, (int)(width/2), (int)(height/2));
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 1.5f, 0f, 0f, -5f, 0f, 1f, 0f);
//Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
drawTriangle(triangleVertices);
//Matrix.translateM(mModelMatrix, 0, 1.5f, 0, -1f);
//Matrix.frustumM(mProjectionMatrix, 0, left, right, -1.0f, 1.0f, 1.0f, 10.0f);
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.setLookAtM(viewMatrix, 0, 1.5f, 0.8f, 0.5f, 0f, 0f, 0f, 0f, 1f, 0f);
GLES20.glViewport((int)(width/2), (int)(height/2), (int)(width/2), (int)(height/2));
drawTriangle(triangleVertices);
drawIntersectionLine();
/*
Matrix.setLookAtM(viewMatrix, 0, 0, 1.5f, 0.5f, 0, 0, 0, 0, 0, -1f);
GLES20.glViewport((int)(width/2), (int)height, (int)(width/2), (int)(height/2));
drawTriangle(triangleVertices);
drawIntersectionLine();
*/
}
private void drawTriangle(final FloatBuffer triangleBuffer)
{
triangleBuffer.position(positionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, positionDataSize, GLES20.GL_FLOAT, false, strideBytes, triangleBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
triangleBuffer.position(colorOffset);
GLES20.glVertexAttribPointer(mColorHandle, colorDataSize, GLES20.GL_FLOAT, false, strideBytes, triangleBuffer);
GLES20.glEnableVertexAttribArray(mColorHandle);
Matrix.multiplyMM(mMVPMatrix, 0, viewMatrix, 0, mModelMatrix, 0);
mMVMatrix = mMVPMatrix;
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
//Log.d("OpenGLES2Test", "The intersection ray is: " + floatArrayAsString(getCameraPos(mMVMatrix)) + " + " + floatArrayAsString(getMouseRayProjection((int)(width / 2), (int)(height / 2), 1.0f, (int)width, (int)height, mMVMatrix, mProjectionMatrix)));
}
private void drawIntersectionLine()
{
lineVertices.position(0);
GLES20.glVertexAttribPointer(mPositionHandle, positionDataSize, GLES20.GL_FLOAT, false, lineStrideBytes, lineVertices);
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glDrawArrays(GLES20.GL_LINES, 0, 2);
}
private void moveIntersectionLineEndPoint(float[] lineEndPoint)
{
this.lineEndPoint = lineEndPoint;
float[] lineVerticesData = {
lineStartPoint[0], lineStartPoint[1], lineStartPoint[2],
lineEndPoint[0], lineEndPoint[1], lineEndPoint[2]
};
lineVertices = ByteBuffer.allocateDirect(lineVerticesData.length * bytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
lineVertices.put(lineVerticesData).position(0);
}
Although I'm pretty sure my 4x4 matrix by 4d vector multiplication method is correct, here is that method as well just in case:
public float[] multiplyMat4ByVec4(float[] matrix4, float[] vector4)
{
float[] returnMatrix = new float[4];
returnMatrix[0] = (matrix4[0] * vector4[0]) + (matrix4[1] * vector4[1]) + (matrix4[2] * vector4[2]) + (matrix4[3] * vector4[3]);
returnMatrix[1] = (matrix4[4] * vector4[0]) + (matrix4[5] * vector4[1]) + (matrix4[6] * vector4[2]) + (matrix4[7] * vector4[3]);
returnMatrix[2] = (matrix4[8] * vector4[0]) + (matrix4[9] * vector4[1]) + (matrix4[10] * vector4[2]) + (matrix4[11] * vector4[3]);
returnMatrix[3] = (matrix4[12] * vector4[0]) + (matrix4[13] * vector4[1]) + (matrix4[14] * vector4[2]) + (matrix4[15] * vector4[3]);
return returnMatrix;
}
The goal of this test app is to show the scene from a few separate angles so that I can see how the intersection line looks based on my code. I wanted to draw the line starting at the camera's origin and ending at the intersection point, but it's acting oddly. The endpoint seems to be being pushed farther along the x axis in the positive direction than it should be, and in some spots it's seems to sort of...skip, as if there were a hole at that location or something. Although I still remember a bit of linear algebra from calculus, I don't remember enough to know exactly what I'm doing here and I've scoured through many of the resources online with no luck. I'm hoping someone that reads this will have more experience dealing with this than I and will be kind enough to help me, or give me any tips if there's something else that I may be doing the wrong or in an inefficient way.
Variable Reference:
Matrices are all float arrays of length 16
mProjectionMatrix = projection matrix
mModelMatrix = model matrix
mMVPMatrix = projection * modelview matrix
mMVMatrix = modelview matrix
private final FloatBuffer triangleVertices;
private FloatBuffer lineVertices;
private final int bytesPerFloat = 4;
private float[] viewMatrix = new float[16];
private static Context context;
private int mMVPMatrixHandle;
private int mPositionHandle;
private int mColorHandle;
private float[] mProjectionMatrix = new float[16];
private float[] mModelMatrix = new float[16];
private float[] mMVPMatrix = new float[16];
private float[] mMVMatrix = new float[16];
private final int strideBytes = 7 * bytesPerFloat;
private final int lineStrideBytes = 3 * bytesPerFloat;
private final int positionOffset = 0;
private final int positionDataSize = 3;
private final int colorOffset = 3;
private final int colorDataSize = 4;
private float width, height;
private float[] lineStartPoint = new float[]{0, 0, 1.5f};
private float[] lineEndPoint = new float[]{0, 0, 0};
After some searching, I found a page that details this process in a different manner. Now I no longer have the issue with the end of the ray jumping to an unexpected position at random times and the end point points to the exact location it should!
Here is the page I used to fix my process:
http://www.antongerdelan.net/opengl/raycasting.html
And here is my final source code for the entire intersection testing app. Most of the relevant code is within the OpenGLRenderer class under the getMouseRayProjection method.
MainActivity.java:
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.Menu;
import android.view.MotionEvent;
public class MainActivity extends Activity {
private MyGLSurfaceView mGLSurfaceView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new MyGLSurfaceView(this);
mGLSurfaceView.setEGLContextClientVersion(2);
mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
OpenGLRenderer renderer = new OpenGLRenderer(this);
mGLSurfaceView.setRenderer(renderer);
mGLSurfaceView.renderer = renderer;
setContentView(mGLSurfaceView);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
protected void onResume() {
super.onResume();
mGLSurfaceView.onResume();
}
#Override
protected void onPause() {
super.onPause();
mGLSurfaceView.onPause();
}
}
class MyGLSurfaceView extends GLSurfaceView {
public OpenGLRenderer renderer;
public float previousX, previousY;
public MyGLSurfaceView(Context context)
{
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent e)
{
float x = e.getX();
float y = e.getY();
switch(e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - previousX;
float dy = y - previousY;
renderer.onTouch(x, y);
}
previousX = x;
previousY = y;
return true;
}
}
OpenGLRenderer.java:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLU;
import android.opengl.Matrix;
import android.opengl.GLSurfaceView;
import android.os.SystemClock;
import android.util.Log;
public class OpenGLRenderer implements GLSurfaceView.Renderer {
private final FloatBuffer triangleVertices;
private FloatBuffer lineVertices;
private final int bytesPerFloat = 4;
private float[] viewMatrix = new float[16];
private static Context context;
private int mMVPMatrixHandle;
private int mPositionHandle;
private int mColorHandle;
private float[] mProjectionMatrix = new float[16];
private float[] mModelMatrix = new float[16];
private float[] mMVPMatrix = new float[16];
private float[] mMVMatrix = new float[16];
private int[] viewport = new int[4];
private final int strideBytes = 7 * bytesPerFloat;
private final int lineStrideBytes = 3 * bytesPerFloat;
private final int positionOffset = 0;
private final int positionDataSize = 3;
private final int colorOffset = 3;
private final int colorDataSize = 4;
private float width, height;
private float[] lineStartPoint = new float[]{0, 0, 1f};
private float[] lineEndPoint = new float[]{0, 0, 0};
private float[] cameraPos = new float[]{0f, 0f, 2.5f};
private float[] cameraLook = new float[]{0f, 0f, -5f};
private float[] cameraUp = new float[]{0f, 1f, 0f};
public OpenGLRenderer(Context context) {
this.context = context;
final float[] triangleVerticesData = {
-0.5f, -0.25f, 0.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.25f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.559016994f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f
};
triangleVertices = ByteBuffer.allocateDirect(triangleVerticesData.length * bytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
triangleVertices.put(triangleVerticesData).position(0);
float[] lineVerticesData = {
lineStartPoint[0], lineStartPoint[1], lineStartPoint[2],
lineEndPoint[0], lineEndPoint[1], lineEndPoint[2]
};
lineVertices = ByteBuffer.allocateDirect(lineVerticesData.length * bytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
lineVertices.put(lineVerticesData).position(0);
}
#Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
Matrix.setLookAtM(viewMatrix, 0, cameraPos[0], cameraPos[1], cameraPos[2], cameraLook[0], cameraLook[1], cameraLook[2], cameraUp[0], cameraUp[1], cameraUp[2]);
try {
int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
if (vertexShaderHandle != 0)
{
GLES20.glShaderSource(vertexShaderHandle, readShader("vertexShader"));
GLES20.glCompileShader(vertexShaderHandle);
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0)
{
GLES20.glDeleteShader(vertexShaderHandle);
vertexShaderHandle = 0;
}
}
if (vertexShaderHandle == 0)
{
throw new RuntimeException("Error creating vertex shader");
}
int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
if (fragmentShaderHandle != 0)
{
GLES20.glShaderSource(fragmentShaderHandle, readShader("fragmentShader"));
GLES20.glCompileShader(fragmentShaderHandle);
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0)
{
GLES20.glDeleteShader(fragmentShaderHandle);
fragmentShaderHandle = 0;
}
}
if (fragmentShaderHandle == 0)
{
throw new RuntimeException("Error creating fragment shader.");
}
int programHandle = GLES20.glCreateProgram();
if (programHandle != 0)
{
GLES20.glAttachShader(programHandle, vertexShaderHandle);
GLES20.glAttachShader(programHandle, fragmentShaderHandle);
GLES20.glBindAttribLocation(programHandle, 0, "a_Position");
GLES20.glBindAttribLocation(programHandle, 1, "a_Color");
GLES20.glLinkProgram(programHandle);
final int[] linkStatus = new int[1];
GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == 0)
{
GLES20.glDeleteProgram(programHandle);
programHandle = 0;
}
}
if (programHandle == 0)
{
throw new RuntimeException("Error creating program.");
}
mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");
mColorHandle = GLES20.glGetAttribLocation(programHandle, "a_Color");
GLES20.glUseProgram(programHandle);
} catch (IOException e)
{
Log.d("OpenGLES2Test", "The shader could not be read: " + e.getMessage());
} catch (RuntimeException e)
{
Log.d("OpenGLES2Test", e.getMessage());
}
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
GLES20.glDepthMask(true);
}
#Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES20.glViewport(0, 0, width/2, height/2);
this.width = width;
this.height = height;
final float ratio = (float) width / height;
final float left = -ratio;
final float right = ratio;
final float bottom = -1.0f;
final float top = 1.0f;
final float near = 1.0f;
final float far = 10.0f;
GLES20.glGetIntegerv(GLES20.GL_VIEWPORT, viewport, 0);
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
}
#Override
public void onDrawFrame(GL10 gl10) {
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
long time = SystemClock.uptimeMillis() % 10000L;
GLES20.glViewport(0, 0, (int)(width), (int)(height));
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.setLookAtM(viewMatrix, 0, cameraPos[0], cameraPos[1], cameraPos[2], cameraLook[0], cameraLook[1], cameraLook[2], cameraUp[0], cameraUp[1], cameraUp[2]);
Matrix.multiplyMM(mMVMatrix, 0, viewMatrix, 0, mModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
drawTriangle(triangleVertices);
drawIntersectionLine();
}
private void drawTriangle(final FloatBuffer triangleBuffer)
{
triangleBuffer.position(positionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, positionDataSize, GLES20.GL_FLOAT, false, strideBytes, triangleBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
triangleBuffer.position(colorOffset);
GLES20.glVertexAttribPointer(mColorHandle, colorDataSize, GLES20.GL_FLOAT, false, strideBytes, triangleBuffer);
GLES20.glEnableVertexAttribArray(mColorHandle);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}
private void drawIntersectionLine()
{
lineVertices.position(0);
GLES20.glVertexAttribPointer(mPositionHandle, positionDataSize, GLES20.GL_FLOAT, false, lineStrideBytes, lineVertices);
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glDrawArrays(GLES20.GL_LINES, 0, 2);
}
private void moveIntersectionLineEndPoint(float[] lineEndPoint)
{
this.lineEndPoint = lineEndPoint;
float[] lineVerticesData = {
lineStartPoint[0], lineStartPoint[1], lineStartPoint[2],
lineEndPoint[0], lineEndPoint[1], lineEndPoint[2]
};
lineVertices = ByteBuffer.allocateDirect(lineVerticesData.length * bytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
lineVertices.put(lineVerticesData).position(0);
}
public static String readShader(String filePath) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(context.getAssets().open(filePath)));
StringBuilder sb = new StringBuilder();
String line;
while( ( line = reader.readLine() ) != null)
{
sb.append(line + "\n");
}
reader.close();
return sb.toString();
}
public float[] getMouseRayProjection(float touchX, float touchY, float windowWidth, float windowHeight, float[] view, float[] projection)
{
float[] rayDirection = new float[4];
float normalizedX = 2f * touchX/windowWidth - 1f;
float normalizedY = 1f - 2f*touchY/windowHeight;
float normalizedZ = 1.0f;
float[] rayNDC = new float[]{normalizedX, normalizedY, normalizedZ};
float[] rayClip = new float[]{rayNDC[0], rayNDC[1], -1f, 1f};
float[] inverseProjection = new float[16];
Matrix.invertM(inverseProjection, 0, projection, 0);
float[] rayEye = multiplyMat4ByVec4(inverseProjection, rayClip);
rayClip = new float[]{rayClip[0], rayClip[1], -1f, 0f};
float[] inverseView = new float[16];
Matrix.invertM(inverseView, 0, view, 0);
float[] rayWorld4D = multiplyMat4ByVec4(inverseView, rayEye);
float[] rayWorld = new float[]{rayWorld4D[0], rayWorld4D[1], rayWorld4D[2]};
rayDirection = normalizeVector3(rayWorld);
return rayDirection;
}
public float[] normalizeVector3(float[] vector3)
{
float[] normalizedVector = new float[3];
float magnitude = (float) Math.sqrt((vector3[0] * vector3[0]) + (vector3[1] * vector3[1]) + (vector3[2] * vector3[2]));
normalizedVector[0] = vector3[0] / magnitude;
normalizedVector[1] = vector3[1] / magnitude;
normalizedVector[2] = vector3[2] / magnitude;
return normalizedVector;
}
/*
public float[] getMouseRayProjection(float touchX, float touchY, float windowWidth, float windowHeight, float[] modelView, float[] projection)
{
float[] rayDirection = new float[4];
float normalizedX = 2 * touchX/windowWidth - 1;
float normalizedY = 1 - 2*touchY/windowHeight;
float[] unviewMatrix = new float[16];
float[] viewMatrix = new float[16];
Matrix.multiplyMM(viewMatrix, 0, projection, 0, modelView, 0);
Matrix.invertM(unviewMatrix, 0, viewMatrix, 0);
float[] nearPoint = multiplyMat4ByVec4(unviewMatrix, new float[]{normalizedX, normalizedY, 0, 1});
float[] modelviewInverse = new float[16];
Matrix.invertM(modelviewInverse, 0, modelView, 0);
float[] cameraPos = new float[4];
cameraPos[0] = modelviewInverse[12];
cameraPos[1] = modelviewInverse[13];
cameraPos[2] = modelviewInverse[14];
cameraPos[3] = modelviewInverse[15];
rayDirection[0] = (nearPoint[0] - cameraPos[0]);
rayDirection[1] = (nearPoint[1] - cameraPos[1]);
rayDirection[2] = (nearPoint[2] - cameraPos[2]);
rayDirection[3] = (nearPoint[3] - cameraPos[3]);
return rayDirection;
}
*/
/*
public float[] getOGLPosition(int x, int y)
{
GLU.gluUnProject(x, y, 0, , modelOffset, project, projectOffset, view, viewOffset, obj, objOffset)
}
*/
public float[] getCameraPos(float[] modelView)
{
float[] modelviewInverse = new float[16];
Matrix.invertM(modelviewInverse, 0, modelView, 0);
float[] cameraPos = new float[4];
cameraPos[0] = modelviewInverse[12];
cameraPos[1] = modelviewInverse[13];
cameraPos[2] = modelviewInverse[14];
cameraPos[3] = modelviewInverse[15];
return cameraPos;
}
public String floatArrayAsString(float[] array)
{
StringBuilder sb = new StringBuilder();
sb.append("[");
for (Float f : array)
{
sb.append(f + ", ");
}
sb.deleteCharAt(sb.length() - 1);
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
return sb.toString();
}
public float[] getInverseMatrix(float[] originalMatrix)
{
float[] inverseMatrix = new float[16];
Matrix.invertM(inverseMatrix, 0, originalMatrix, 0);
return inverseMatrix;
}
public float[] multiplyMat4ByVec4(float[] matrix4, float[] vector4)
{
float[] returnMatrix = new float[4];
returnMatrix[0] = (matrix4[0] * vector4[0]) + (matrix4[1] * vector4[1]) + (matrix4[2] * vector4[2]) + (matrix4[3] * vector4[3]);
returnMatrix[1] = (matrix4[4] * vector4[0]) + (matrix4[5] * vector4[1]) + (matrix4[6] * vector4[2]) + (matrix4[7] * vector4[3]);
returnMatrix[2] = (matrix4[8] * vector4[0]) + (matrix4[9] * vector4[1]) + (matrix4[10] * vector4[2]) + (matrix4[11] * vector4[3]);
returnMatrix[3] = (matrix4[12] * vector4[0]) + (matrix4[13] * vector4[1]) + (matrix4[14] * vector4[2]) + (matrix4[15] * vector4[3]);
return returnMatrix;
}
public void onTouch(float touchX, float touchY)
{
float[] mouseRayProjection = getMouseRayProjection(touchX, touchY, width, height, mMVMatrix, mProjectionMatrix);
Log.d("OpenGLES2Test", "Mouse Ray: " + floatArrayAsString(mouseRayProjection));
//Log.d("OpenGLES2Test", "ModelView: " + floatArrayAsString(mMVMatrix));
//Log.d("OpenGLES2Test", "ModelViewInverse: " + floatArrayAsString(getInverseMatrix(mMVMatrix)));
//Log.d("OpenGLES2Test", "Mouse Coordinates: " + touchX + ", " + touchY);
//Log.d("OpenGLES2Test", "Ray Coordinates: " + mouseRayProjection[0] + ", " + mouseRayProjection[1] + ", " + mouseRayProjection[2] + ", " + mouseRayProjection[3]);
moveIntersectionLineEndPoint(mouseRayProjection);
}
}
fragmentShader:
precision mediump float;
varying vec4 v_Color;
void main()
{
gl_FragColor = v_Color;
}
vertexShader:
uniform mat4 u_MVPMatrix;
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main()
{
v_Color = a_Color;
gl_Position = u_MVPMatrix * a_Position;
}
your codes are good but it does not work fine for me. After studying the code from Rajawali ( https://github.com/MasDennis/Rajawali ), I would suggest the following changes in your final codes:
public float[] getMouseRayProjection(float touchX, float touchY, float windowWidth, float windowHeight, float[] view, float[] projection)
{
float[] rayDirection = new float[4];
float normalizedX = 2f * touchX/windowWidth - 1f;
float normalizedY = 1f - 2f*touchY/windowHeight;
float normalizedZ = 1.0f;
float[] rayClip1 = new float[]{normalizedX, normalizedY, -1, 1};
float[] rayClip2 = new float[]{normalizedX, normalizedY, 1, 1};
float[] mVPMatrix = new float[16];
float[] invertVPMatrix = new float[16];
Matrix.multiplyMM(mVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.invertM(invertVPMatrix, 0, mVPMatrix, 0);
float[] rayWorld1 = new float[4];
Matrix.multiplyMV(rayWorld1, 0, invertVPMatrix, 0, rayClip1, 0);
float[] rayWorld2 = new float[4];
Matrix.multiplyMV(rayWorld2, 0, invertVPMatrix, 0, rayClip2, 0);
if (rayWorld1[3]!=0 && rayWorld2[3]!=0)
{
rayWorld1[0] = rayWorld1[0] / rayWorld1[3];
rayWorld1[1] = rayWorld1[1] / rayWorld1[3];
rayWorld1[2] = rayWorld1[2] / rayWorld1[3];
rayWorld1[3] = 1;
rayWorld2[0] = rayWorld2[0] / rayWorld2[3];
rayWorld2[1] = rayWorld2[1] / rayWorld2[3];
rayWorld2[2] = rayWorld2[2] / rayWorld2[3];
rayWorld2[3] = 1;
}
.... continue determine which object has intersection with the ray.
I think the most important part is that you should divide the vector by the W element.