How to locate texture on sphere in jME3? - java

I would like to place JPEG texture map on sphere. It works for me, but I want to rotate texture by 180 degrees. I.e I want image to start not from zero UV coordinates, but earlier.
UPDATE
I have tried to reassign texture coordinates of a sphere. Texture coordinates are float, and I was hoping they are not constrained to the range of [0..1]. Otherwise it should placed my image into the region of [0..1 x 0..1].
It did something like latter, but not precise:
I.e. entire image was put into small region of a sphere. But, this exact region, where it is located, corresponds with negative values of U, i.e. at the same longitude, where image margin was in previous experiment (top sphere).
Why?
Image is here: https://en.wikipedia.org/wiki/File:Equirectangular_projection_SW.jpg
The code is follows:
package tests.com.jme3;
import java.nio.FloatBuffer;
import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapText;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.scene.shape.Sphere;
import com.jme3.util.BufferUtils;
public class Try_TextureTransform extends SimpleApplication {
public static void main(String[] args) {
Try_TextureTransform app = new Try_TextureTransform();
app.setShowSettings(false);
app.start(); // start the game
}
final float speed = 0.01f;
BitmapText hudText;
Sphere sphere1Mesh, sphere2Mesh;
Material sphere1Mat, sphere2Mat;
Geometry sphere1Geo, sphere2Geo;
Quaternion orientation;
DirectionalLight sun;
#Override
public void simpleInitApp() {
flyCam.setEnabled(false);
setDisplayStatView(false);
setDisplayFps(false);
hudText = new BitmapText(guiFont, false);
hudText.setSize(guiFont.getCharSet().getRenderedSize()); // font size
hudText.setColor(ColorRGBA.Blue); // font color
hudText.setText(""); // the text
hudText.setLocalTranslation(300, hudText.getLineHeight()*2, 0); // position
guiNode.attachChild(hudText);
sphere1Mesh = new Sphere(50, 50, 2);
sphere1Mesh.setTextureMode(Sphere.TextureMode.Projected); // matrc
sphere1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
sphere1Mat.setTexture("ColorMap", assetManager.loadTexture("textures/Equirectangular_projection_SW.jpg"));
sphere1Geo = new Geometry("Sphere2", sphere1Mesh);
sphere1Geo.setMaterial(sphere1Mat);
sphere1Geo.setLocalTranslation(0, 0, 2);
sphere2Mesh = new Sphere(50, 50, 2);
VertexBuffer vb = sphere2Mesh.getBuffer(Type.Position);
FloatBuffer fb = (FloatBuffer) vb.getData();
float[] vertexCoordinates = BufferUtils.getFloatArray(fb);
VertexBuffer vb2 = sphere2Mesh.getBuffer(Type.TexCoord);
FloatBuffer fb2 = (FloatBuffer) vb2.getData();
float[] uvCoordinates = BufferUtils.getFloatArray(fb2);
double rho;
for (int i = 0; i < vertexCoordinates.length/3; ++i) {
uvCoordinates[i*2] = (float) Math.atan2(vertexCoordinates[i*3+1], vertexCoordinates[i*3]);
rho = Math.sqrt(Math.pow( vertexCoordinates[i*3], 2) + Math.pow( vertexCoordinates[i*3+1], 2));
uvCoordinates[i*2+1] = (float) Math.atan2(vertexCoordinates[i*3+2], rho);
}
//apply new texture coordinates
VertexBuffer uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
uvCoordsBuffer.setupData(Usage.Static, 2, com.jme3.scene.VertexBuffer.Format.Float, BufferUtils.createFloatBuffer(uvCoordinates));
sphere2Mesh.clearBuffer(Type.TexCoord);
sphere2Mesh.setBuffer(uvCoordsBuffer);
//sphere2Mesh.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
sphere2Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
sphere2Mat.setTexture("ColorMap", assetManager.loadTexture("textures/Equirectangular_projection_SW.jpg"));
sphere2Geo = new Geometry("Sphere2", sphere2Mesh);
sphere2Geo.setMaterial(sphere2Mat);
sphere2Geo.setLocalTranslation(0, 0, -2);
cam.setLocation(new Vector3f(-10, 0, 0));
cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Z);
rootNode.attachChild(sphere1Geo);
rootNode.attachChild(sphere2Geo);
}
#Override
public void simpleUpdate(float tpf) {
Vector2f cursorPosition = inputManager.getCursorPosition();
Vector3f cursorPositionWorld = cam.getWorldCoordinates(cursorPosition, 1);
orientation = new Quaternion().fromAngleAxis(cursorPositionWorld.z*speed, Vector3f.UNIT_Y);
orientation.multLocal(new Quaternion().fromAngleAxis(-cursorPositionWorld.y*speed, Vector3f.UNIT_Z));
rootNode.setLocalRotation(orientation);
}
}

The correct way to do this is just to rotate the geometry as you see fit or edit the texture (techniques 1 and 2) but because you talk about modifying the texture coordinates themselves I include techniques 3 and 4 in case you are using this example to learn a larger technique for when it is appropriate.
Technique 1 - Rotate the geometry
Rotate the geometry so that it is orientated the way you want it. This is by far the easiest, most appropriate and most understandable technique and what I recommend
//Add this
Quaternion quat=new Quaternion();
quat.fromAngles(0 ,0 , FastMath.PI);
sphere1Geo.setLocalRotation(quat);
Complete program
public class Main extends SimpleApplication {
public static void main(String[] args) {
Main app = new Main();
app.setShowSettings(false);
app.start(); // start the game
}
final float speed = 0.01f;
BitmapText hudText;
Quaternion orientation;
DirectionalLight sun;
#Override
public void simpleInitApp() {
flyCam.setEnabled(false);
setDisplayStatView(false);
setDisplayFps(false);
hudText = new BitmapText(guiFont, false);
hudText.setSize(guiFont.getCharSet().getRenderedSize()); // font size
hudText.setColor(ColorRGBA.Blue); // font color
hudText.setText(""); // the text
hudText.setLocalTranslation(300, hudText.getLineHeight()*2, 0); // position
guiNode.attachChild(hudText);
cam.setLocation(new Vector3f(10, 0, 0));
cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Z);
addOriginalSphere();
addRotatedSphere();
}
public void addOriginalSphere(){
Sphere sphere1Mesh = new Sphere(50, 50, 2);
sphere1Mesh.setTextureMode(Sphere.TextureMode.Projected); // matrc
Material sphere1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
sphere1Mat.setTexture("ColorMap", assetManager.loadTexture("Textures/world.png"));
Geometry sphere1Geo = new Geometry("Original Sphere", sphere1Mesh);
sphere1Geo.setMaterial(sphere1Mat);
sphere1Geo.setLocalTranslation(0, -2, 0);
rootNode.attachChild(sphere1Geo);
}
public void addRotatedSphere(){
Sphere sphere1Mesh = new Sphere(50, 50, 2);
sphere1Mesh.setTextureMode(Sphere.TextureMode.Projected); // matrc
Material sphere1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
sphere1Mat.setTexture("ColorMap", assetManager.loadTexture("Textures/world.png"));
Geometry sphere1Geo = new Geometry("Rotated Sphere", sphere1Mesh);
sphere1Geo.setMaterial(sphere1Mat);
sphere1Geo.setLocalTranslation(0, 2, 0);
//Add this
Quaternion quat=new Quaternion();
quat.fromAngles(0 ,0 , FastMath.PI);
sphere1Geo.setLocalRotation(quat);
rootNode.attachChild(sphere1Geo);
}
#Override
public void simpleUpdate(float tpf) {
}
}
Technique 2 - Edit the texture to conform to the way you want it to be
Many image editing programs exist, the one I use is Paint.Net and (like most editing software) gives exact pixel mouse coordinates. Just cut and paste the image such that greenwich is at the far left. In your case you need to edit the image anyway because it has that horrible white border on it.
Technique 3 - Mess with the vertex texture co-ordinates
This is overkill for this and is not what I recomend. But if this is an excercise to learn to create your own custom mesh then read on
public void addRotatedSphere_ByMessingWithMesh(){
Sphere sphere1Mesh = new Sphere(50, 50, 2);
sphere1Mesh.setTextureMode(Sphere.TextureMode.Projected); // matrc
FloatBuffer textureBuffer=sphere1Mesh.getFloatBuffer(Type.TexCoord);
float[] newTextureCoordinates=new float[textureBuffer.capacity()];
for(int i=0;i<newTextureCoordinates.length;i++){
//texture buffer goes x co-ordinate, y coordinate, x coordinate, y coordinate
if (i%2!=1){
newTextureCoordinates[i]=(float)((textureBuffer.get(i)+0.5)%1);
}else{
newTextureCoordinates[i]=textureBuffer.get(i);
}
}
sphere1Mesh.setBuffer(Type.TexCoord, 2,newTextureCoordinates);
Material sphere1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
sphere1Mat.setTexture("ColorMap", assetManager.loadTexture("Textures/world.png"));
Geometry sphere1Geo = new Geometry("Rotated Sphere", sphere1Mesh);
sphere1Geo.setMaterial(sphere1Mat);
sphere1Geo.setLocalTranslation(0, 2, 0);
rootNode.attachChild(sphere1Geo);
}
This has a problem because the seam at the back is not done properly; because the true texture coordinates go 0,0.2,0.4,0.8,1. Whereas out new ones do a wrap around on the far side. In this specific example you can do a manual handling of the seam but you can already see that this is a pain.
Technique 4 - Write your own shader
This is bordering on rediculus but you could write a custom shader that would take the true texture coordinates and apply a transformation similar to the one performed within Technique 3, but this would be done on the graphics card and is a nightmare to debug.
It goes without saying that that would be using a small nuclear weapon to kill a fly and I shall not explain all the step explicity (but its heavily based on unshaded.j3md and unshaded.vert
Create the following files to define our new material
Material definition
Only change is to mention our custom vertex shader rather than use the custom one
MaterialDef Unshaded {
MaterialParameters {
Texture2D ColorMap
Texture2D LightMap
Color Color (Color)
Boolean VertexColor (UseVertexColor)
Boolean SeparateTexCoord
// Texture of the glowing parts of the material
Texture2D GlowMap
// The glow color of the object
Color GlowColor
// For hardware skinning
Int NumberOfBones
Matrix4Array BoneMatrices
// Alpha threshold for fragment discarding
Float AlphaDiscardThreshold (AlphaTestFallOff)
//Shadows
Int FilterMode
Boolean HardwareShadows
Texture2D ShadowMap0
Texture2D ShadowMap1
Texture2D ShadowMap2
Texture2D ShadowMap3
//pointLights
Texture2D ShadowMap4
Texture2D ShadowMap5
Float ShadowIntensity
Vector4 Splits
Vector2 FadeInfo
Matrix4 LightViewProjectionMatrix0
Matrix4 LightViewProjectionMatrix1
Matrix4 LightViewProjectionMatrix2
Matrix4 LightViewProjectionMatrix3
//pointLight
Matrix4 LightViewProjectionMatrix4
Matrix4 LightViewProjectionMatrix5
Vector3 LightPos
Vector3 LightDir
Float PCFEdge
Float ShadowMapSize
}
Technique {
VertexShader GLSL100: MatDefs/TextureSplitting.vert
FragmentShader GLSL100: Common/MatDefs/Misc/Unshaded.frag
WorldParameters {
WorldViewProjectionMatrix
}
Defines {
SEPARATE_TEXCOORD : SeparateTexCoord
HAS_COLORMAP : ColorMap
HAS_LIGHTMAP : LightMap
HAS_VERTEXCOLOR : VertexColor
HAS_COLOR : Color
NUM_BONES : NumberOfBones
DISCARD_ALPHA : AlphaDiscardThreshold
}
}
Technique {
}
Technique PreNormalPass {
VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert
FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
WorldParameters {
WorldViewProjectionMatrix
WorldViewMatrix
NormalMatrix
}
Defines {
NUM_BONES : NumberOfBones
}
}
Technique PreShadow {
VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert
FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag
WorldParameters {
WorldViewProjectionMatrix
WorldViewMatrix
}
Defines {
COLOR_MAP : ColorMap
DISCARD_ALPHA : AlphaDiscardThreshold
NUM_BONES : NumberOfBones
}
ForcedRenderState {
FaceCull Off
DepthTest On
DepthWrite On
PolyOffset 5 3
ColorWrite Off
}
}
Technique PostShadow15{
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow15.vert
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
}
Defines {
HARDWARE_SHADOWS : HardwareShadows
FILTER_MODE : FilterMode
PCFEDGE : PCFEdge
DISCARD_ALPHA : AlphaDiscardThreshold
COLOR_MAP : ColorMap
SHADOWMAP_SIZE : ShadowMapSize
FADE : FadeInfo
PSSM : Splits
POINTLIGHT : LightViewProjectionMatrix5
NUM_BONES : NumberOfBones
}
ForcedRenderState {
Blend Modulate
DepthWrite Off
PolyOffset -0.1 0
}
}
Technique PostShadow{
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
}
Defines {
HARDWARE_SHADOWS : HardwareShadows
FILTER_MODE : FilterMode
PCFEDGE : PCFEdge
DISCARD_ALPHA : AlphaDiscardThreshold
COLOR_MAP : ColorMap
SHADOWMAP_SIZE : ShadowMapSize
FADE : FadeInfo
PSSM : Splits
POINTLIGHT : LightViewProjectionMatrix5
NUM_BONES : NumberOfBones
}
ForcedRenderState {
Blend Modulate
DepthWrite Off
PolyOffset -0.1 0
}
}
Technique Glow {
VertexShader GLSL100: Common/MatDefs/Misc/TextureSplitting.vert
FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
WorldParameters {
WorldViewProjectionMatrix
}
Defines {
NEED_TEXCOORD1
HAS_GLOWMAP : GlowMap
HAS_GLOWCOLOR : GlowColor
NUM_BONES : NumberOfBones
}
}
}
Vertex shader
Use a translation to map the true texture coordinates to the shifted coordinates. Incidently if you think this isn't java; it isn't. Its OpenGL Shader Langauge.
#import "Common/ShaderLib/Skinning.glsllib"
uniform mat4 g_WorldViewProjectionMatrix;
attribute vec3 inPosition;
#if defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
#define NEED_TEXCOORD1
#endif
attribute vec2 inTexCoord;
attribute vec2 inTexCoord2;
attribute vec4 inColor;
varying vec2 texCoord1;
varying vec2 texCoord2;
varying vec4 vertColor;
void main(){
#ifdef NEED_TEXCOORD1
texCoord1 = inTexCoord;
texCoord1.x=texCoord1.x+0.5;
if (texCoord1.x>1){
texCoord1.x=texCoord1.x-1;
}
#endif
#ifdef SEPARATE_TEXCOORD
texCoord2 = inTexCoord2;
#endif
#ifdef HAS_VERTEXCOLOR
vertColor = inColor;
#endif
vec4 modelSpacePos = vec4(inPosition, 1.0);
#ifdef NUM_BONES
Skinning_Compute(modelSpacePos);
#endif
gl_Position = g_WorldViewProjectionMatrix * modelSpacePos;
}
Then use this as a material instead of unshaded.j3md
Material sphere1Mat = new Material(assetManager, "Materials/TextureSplitting.j3md");
Again there is a nasty break around the back where the true texture roles over between 0 and 1 which we could handle explicitly if we wanted but we'd have to make sure there were 2 vertexs at the split point one with texture coordinate 0 and one with texture coordinate 1.
Conclusion
Techniques 1 or 2 are the ones you should use. I include techniques 3 and 4 simply to show that you can do this using the actual texture coordinates but that you shouldn't.

Related

OpenGL - difficulty with constructing shapes

I'm starting work on a simple shape-batching system for my 3D engine that will enable me to draw lines and rectangles, etc... with a lower draw call count. I think I've got the basic ideas figured out for the most part, but I'm having problems when I try to draw multiple objects (currently just lines with a thickness you can specify).
Here's a screenshot to show you what I mean:
I'm using indexed rendering with glDrawElements, and two VBOs to represent the vertex data - one for positions, and one for colours.
I construct a line for my shape-batcher by specifying start and end points, like so:
shapeRenderer.begin();
shapeRenderer.setViewMatrix(viewMatrix);
shapeRenderer.setProjectionMatrix(projectionMatrix);
shapeRenderer.setCurrentColour(0, 1f, 0);
shapeRenderer.drawLine(2, 2, 5, 2);
shapeRenderer.setCurrentColour(0, 1f, 1f);
shapeRenderer.drawLine(2, 5, 5, 5);
shapeRenderer.end();
The first line, represented in green in the screenshot, shows perfectly. If I draw only one line it's completely fine. If I were to draw only the second line it would show perfectly as well.
When I call drawLine the following code executes, which I use to compute directions and normals:
private Vector2f temp2fA = new Vector2f();
private Vector2f temp2fB = new Vector2f();
private Vector2f temp2fDir = new Vector2f();
private Vector2f temp2fNrm = new Vector2f();
private Vector2f temp2fTMP = new Vector2f();
private boolean flip = false;
public void drawLine(float xStart, float yStart, float xEnd, float yEnd){
resetLineStates();
temp2fA.set(xStart, yStart);
temp2fB.set(xEnd, yEnd);
v2fDirection(temp2fA, temp2fB, temp2fDir);
v2fNormal(temp2fDir, temp2fNrm);
float halfThickness = currentLineThickness / 2;
//System.out.println("new line called");
v2fScaleAndAdd(temp2fB, temp2fNrm, -halfThickness, temp2fTMP);
pushVertex(temp2fTMP);
v2fScaleAndAdd(temp2fB, temp2fNrm, halfThickness, temp2fTMP);
pushVertex(temp2fTMP);
v2fScaleAndAdd(temp2fA, temp2fNrm, halfThickness, temp2fTMP);
pushVertex(temp2fTMP);
v2fScaleAndAdd(temp2fA, temp2fNrm, -halfThickness, temp2fTMP);
pushVertex(temp2fTMP);
//System.out.println(indexCount + " before rendering.");
int index = indexCount;
pushIndices(index, index + 1, index + 3);
pushIndices(index + 1, index + 2, index + 3);
//System.out.println(indexCount + " after rendering.");
}
private void resetLineStates(){
temp2fA.set(0);
temp2fB.set(0);
temp2fDir.set(0);
temp2fNrm.set(0);
temp2fTMP.set(0);
}
pushIndices is the following function:
private void pushIndices(int i1, int i2, int i3){
shapeIndices.add(i1);
shapeIndices.add(i2);
shapeIndices.add(i3);
indexCount += 3;
}
And pushVertex works like so:
private void pushVertex(float x, float y, float z){
shapeVertexData[vertexDataOffset] = x;
shapeColourData[vertexDataOffset] = currentShapeColour.x;
shapeVertexData[vertexDataOffset + 1] = y;
shapeColourData[vertexDataOffset + 1] = currentShapeColour.y;
shapeVertexData[vertexDataOffset + 2] = z;
shapeColourData[vertexDataOffset + 2] = currentShapeColour.z;
//System.out.println("\tpushed vertex: " + data.x + ", " + data.y + ", 0");
vertexDataOffset += 3;
}
I'm using the following fields to store vertex data and such - this is all sub-buffered to a VBO when I flush the batch. If the vertex data arrays have not had to grow in size, I will sub-buffer them to their respective VBO, likewise with the element buffer, otherwise if they have had to grow then I re-buffer the VBO to fit.
private float[] shapeVertexData;
private float[] shapeColourData;
private int vertexDataOffset;
private ArrayList<Integer> shapeIndices;
private int indexCount;
When I use my debugger in IDEA, the vertex data appears completely correct in the arrays I'm constructing, but when I explore it in RenderDoc, it's wrong. I don't understand what I'm doing wrong to get these results, and obviously the first two vertices appear completely fine even for the second rectangle, but the others are totally wrong.
I'm confident that my shaders are not the problem, as they're very simple, but here they are:
shape_render.vs (vertex shader):
#version 330
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec3 aColour;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
flat out vec3 shapeFill;
void main(){
shapeFill = aColour;
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(aPosition.x, aPosition.y, 0.0, 1.0);
}
shape_render.fs (fragment shader):
#version 330
layout (location = 0) out vec4 fragColour;
in vec3 shapeFill;
void main(){
fragColour = vec4(shapeFill, 1);
}
I think I've just about explained it to the best of my knowledge, any insight would be greatly appreciated. I've already checked and determined I'm enabling the necessary vertex arrays, etc... and rendering the correct amount of indices (12):
Thanks so much for having a look at this for me.
I figured it out after thinking about it for a while longer. It was to do with how I was specifying the indices. I was using the correct amount of indices however specifying them incorrectly.
For arguments sake to construct a triangle, the first one would have an index count of 0, and with four vertices, the indices would be 1,2,3 and 2,3,1 (for example). However for each new triangle I was starting the index count at the old count plus six, which makes sense for addressing an array, but because each rectangle only specified four vertices, I was pointing indices at data that didn’t exist.
So instead of using indexCount += 3 each time I pushed indices, I’ll get the current count of vertices instead and build my indices from that.

Android OpenGL ES 2.0, Compute Normal for each triangle

I need to compute Normals for each Triangle Face (not for each vertex) using Opengl ES 2.0 in Android. But I can't pass attribute in the Fragment Shader directly.
Found one solution: Repeat vertices for each Triangle, and pass Triangle face Normal as an Attribute in Vertex Shader.
But I don't want to duplicate the vertices. I am drawing triangle using Vertex Indices.
So, a vertex is shared by more than one triangle, then how should I compute Triangle face Normals.
p.s. I am a newbie in opengl.
The easiest solution is just to duplicate vertices. The vertex shader is very rarely a bottleneck. I do not know your specific needs, however, there are cirumstances when duplicating a vertex is not a good solution. For example, if the mesh is skinned and animated that means a lot of computation happens in the vertex shader. Another case is when the mesh is animated in some weird way in the vertex shader and you have to recompute the normals. Clearly, you cannot compute per face normals in the vertex shader. You could do that in the geometry shader, but we do not have one in OpenGL ES 2.0. However, there is a simple solution - compute normals in fragment shader! So, if duplication of vertices does not work for you, here is the solution:
We will need an OpenGL extension - standard_derivatives, which is widely supported, but you will still need to check if it is supported on the device before running the code. To enable the extension, you will have to add the following line to the fragment shader before it's code:
#extension GL_OES_standard_derivatives : enable
We will need a varying variable for the vertex position in the world coordinates. It should be computed in the vertex shader and how it is done depends much on your shader. It is used for many needs, so you may already compute it in your vertex shader. So let's assume, that we have this line in the fragment shader:
varying vec3 positionWorld;
We will need a view matrix of the camera. It is possible that you are already passing one to the fragment shader. Let's assume that we have this uniform in the fragment shader:
uniform mat4 viewMatrix;
Now, we are going to compute the normal. First, we compute a normal in view space and then transform into the worldspace. To compute normal in the viewspace, we use derivative functions:
vec3 normalViewSpace = normalize(cross(dFdx(positionWorldSpace), dFdy(positionWorldSpace)));
Here a derivative of position is taken with respect to x and y coordinate in screen space. That means that we have two vectors that are in lay in the surface plane. To get normal to the surface we do a cross product. Sure, the result is not a unit vector, so we need also to normalize it.
The last step is to compute normal in the worldspace. View matrix applies a transformation from world space to view space. One could think that we need to compute the inverse of it since we need to go from view space to world space, but because view matrix is orthonormal, the transpose of that matrix is also its inverse, so the code will be:
vec3 normalWorldSpace = (vec4(normalViewSpace, 0.0) * viewMatrix).xyz;
To make life easier, we can wrap everything into a function:
vec3 ReconstructNormal(vec3 positionWorldSpace)
{
vec3 normalViewSpace = normalize(cross(dFdx(positionWorldSpace), dFdy(positionWorldSpace)));
vec3 normalWorldSpace = (vec4(normalViewSpace, 0.0) * viewMatrix).xyz;
return normalWorldSpace;
}
Now we have a reconstructed normal in world space. Below, just a simple example, why this can be very useful. Note, that since it uses WebGL, it is also pretty much OpenGL ES 2.0 compatible.
var container;
var camera, scene, renderer;
var mesh;
var uniforms;
var clock = new THREE.Clock();
init();
animate();
function init() {
container = document.getElementById('container');
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 0.6;
camera.position.y = 0.2;
camera.rotation.x = -0.45;
scene = new THREE.Scene();
var boxGeometry = new THREE.PlaneGeometry(0.75, 0.75, 32, 32);
var heightMap = THREE.ImageUtils.loadTexture("data:image/png;base64,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");
heightMap.wrapT = heightMap.wrapS = THREE.RepeatWrapping;
uniforms = {u_time: {type: "f", value: 0.0 }, u_heightMap: {type: "t",value:heightMap} };
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
side: THREE.DoubleSide,
wireframe: false,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragment_shader').textContent
});
mesh = new THREE.Mesh(boxGeometry, material);
mesh.rotation.x = 3.14 / 2.0;
scene.add(mesh);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0x000000, 1 );
container.appendChild(renderer.domElement);
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize(event) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
var delta = clock.getDelta();
uniforms.u_time.value += delta;
//mesh.rotation.z += delta * 0.5;
renderer.render(scene, camera);
}
body { margin: 0px; overflow: hidden; }
<script src="https://threejs.org/build/three.min.js"></script>
<div id="container"></div>
<script id="fragment_shader" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
varying vec3 positionWorld; // position of vertex in world coordinates
vec3 ReconstructNormal(vec3 positionWorldSpace)
{
vec3 normalViewSpace = normalize(cross(dFdx(positionWorldSpace), dFdy(positionWorldSpace)));
vec3 normalWorldSpace = (vec4(normalViewSpace, 0.0) * viewMatrix).xyz;
return normalWorldSpace;
}
// Just some example of using a normal. Here we do a really simple shading
void main( void )
{
vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
vec3 normal = ReconstructNormal(positionWorld);
float diffuse = max(dot(lightDir, normal), 0.0);
vec3 albedo = vec3(0.2, 0.4, 0.7);
gl_FragColor = vec4(albedo * diffuse, 1.0);
}
</script>
<script id="vertexShader" type="x-shader/x-vertex">
uniform lowp sampler2D u_heightMap;
uniform float u_time;
varying vec3 positionWorld;
// Example of vertex shader that moves vertices
void main()
{
vec3 pos = position;
vec2 offset1 = vec2(1.0, 0.5) * u_time * 0.01;
vec2 offset2 = vec2(0.5, 1.0) * u_time * 0.01;
float hight1 = texture2D(u_heightMap, uv + offset1).r * 0.02;
float hight2 = texture2D(u_heightMap, uv + offset2).r * 0.02;
pos.z += hight1 + hight2;
vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );
positionWorld = mvPosition.xyz;
gl_Position = projectionMatrix * mvPosition;
}
</script>

Get PeasyCam to follow a sphere shape as it orbits another sphere in Processing

Sorry for all the code, but basically I am trying to set up a camera to follow a planet as it orbits around it's sun. How do I get the camera to follow the planet? The rotations in draw do not move the camera at all.
import peasy.*;
import peasy.org.apache.commons.math.*;
import peasy.org.apache.commons.math.geometry.*;
import peasy.test.*;
float sunRadius, planetRadius;
PImage mercury;
PImage the_sun;
PVector sunCenter;
PVector spoke;
Planet ourSun;
Planet ourPlanet;
float orbitSpeed = 0;
PeasyCam camera;
void setup()
{
size(1000,700, P3D);
sunRadius = 200;
planetRadius = 50;
mercury = loadImage("planet2.png");
the_sun = loadImage("sun.jpg");
sunCenter = new PVector(width/2, height/2, -500);
spoke = new PVector(1,0,1);
ourSun = new Planet(the_sun, sunRadius);
ourPlanet = new Planet(mercury, planetRadius);
}
void draw()
{
background(0);
ourSun.show(sunCenter);
ourPlanet.orbit(sunCenter, spoke, orbitSpeed, 1000);
pushMatrix();
rotateY(orbitSpeed);
camera = new PeasyCam(this, sunCenter.x, sunCenter.y, sunCenter.z, 4000);
camera.setActive(false);
popMatrix();
orbitSpeed += 0.01;
}
class Planet {
float sizeof;
PShape planet;
Planet(PImage img, float sizeof)
{
noStroke();
noFill();
this.sizeof = sizeof;
planet = createShape(SPHERE, sizeof);
planet.setTexture(img);
}
void show(PVector position)
{
pushMatrix();
translate(position.x, position.y, position.z);
shape(planet);
popMatrix();
}
void orbit(PVector parent, PVector spoke, float speed, float distance)
{
pushMatrix();
translate(parent.x, parent.y, parent.z);
PVector traj = new PVector(parent.x-distance, 0, 0);
PVector axis = traj.cross(spoke);
rotate(speed, axis.x, axis.y, axis.z);
translate(traj.x, traj.y, traj.z);
shape(planet);
popMatrix();
}
}
I am having difficulty calculating the planet sphere's center point as it rotates and translates and also getting the peasycam to rotate about any axis successfully.
I have done something similar in MapleMath, there I had the moon rotating around earth and the two were orbiting the Sun. It is easy using Parametric equations . You want the camera to orbit Earth or to orbit the Sun . Your camera is located in a fixed position. Well beyond Earth.
I am just starting to learn Processing which already proves to have superior visuals. May I suggest that you use a Math program running Parametrics that will generate output tables of points
or vertices and cut and paste the table after beginShape. I have downloaded your code, and will work on it.
I am actually attempting to construct something very similar. I will be happy to share sometime soon.
vib
vburach#shaw.ca

Worldwind Custom Renderable Picking Issue

I'm going through this tutorial
Whenever my mouse hovers over the cube created with this code (my version below), the Atmosphere and Stars disappear.
This is how it looks normally:
This is how it looks when I hover over the cube (Look at the atmosphere):
I'm not sure what is going on here.
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwindx.examples.tutorial;
import gov.nasa.worldwind.Configuration;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.pick.PickSupport;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.OGLUtil;
import gov.nasa.worldwindx.examples.ApplicationTemplate;
import javax.media.opengl.*;
import java.awt.*;
/**
* Example of a custom {#link Renderable} that draws a cube at a geographic position. This class shows the simplest
* possible example of a custom Renderable, while still following World Wind best practices. See
* http://goworldwind.org/developers-guide/how-to-build-a-custom-renderable/ for a complete description of this
* example.
*
* #author pabercrombie
* #version $Id: Cube.java 691 2012-07-12 19:17:17Z pabercrombie $
*/
public class Cube extends ApplicationTemplate implements Renderable
{
/** Geographic position of the cube. */
protected Position position;
/** Length of each face, in meters. */
protected double size;
/** Support object to help with pick resolution. */
protected PickSupport pickSupport = new PickSupport();
// Determined each frame
protected long frameTimestamp = -1L;
protected OrderedCube currentFramesOrderedCube;
/**
* This class holds the Cube's Cartesian coordinates. An instance of it is added to the scene controller's ordered
* renderable queue during picking and rendering.
*/
protected class OrderedCube implements OrderedRenderable
{
/** Cartesian position of the cube, computed from
* {#link gov.nasa.worldwindx.examples.tutorial.Cube#position}. */
protected Vec4 placePoint;
/** Distance from the eye point to the cube. */
protected double eyeDistance;
/**
* The cube's Cartesian bounding extent.
*/
protected Extent extent;
public double getDistanceFromEye()
{
return this.eyeDistance;
}
public void pick(DrawContext dc, Point pickPoint)
{
// Use same code for rendering and picking.
this.render(dc);
}
public void render(DrawContext dc)
{
Cube.this.drawOrderedRenderable(dc, Cube.this.pickSupport);
}
}
public Cube(Position position, double sizeInMeters)
{
this.position = position;
this.size = sizeInMeters;
}
public void render(DrawContext dc)
{
// Render is called twice, once for picking and once for rendering. In both cases an OrderedCube is added to
// the ordered renderable queue.
OrderedCube orderedCube = this.makeOrderedRenderable(dc);
if (orderedCube.extent != null)
{
if (!this.intersectsFrustum(dc, orderedCube))
return;
// If the shape is less that a pixel in size, don't render it.
if (dc.isSmall(orderedCube.extent, 1))
return;
}
// Add the cube to the ordered renderable queue. The SceneController sorts the ordered renderables by eye
// distance, and then renders them back to front.
dc.addOrderedRenderable(orderedCube);
}
/**
* Determines whether the cube intersects the view frustum.
*
* #param dc the current draw context.
*
* #return true if this cube intersects the frustum, otherwise false.
*/
protected boolean intersectsFrustum(DrawContext dc, OrderedCube orderedCube)
{
if (dc.isPickingMode())
return dc.getPickFrustums().intersectsAny(orderedCube.extent);
return dc.getView().getFrustumInModelCoordinates().intersects(orderedCube.extent);
}
/**
* Compute per-frame attributes, and add the ordered renderable to the ordered renderable list.
*
* #param dc Current draw context.
*/
protected OrderedCube makeOrderedRenderable(DrawContext dc)
{
// This method is called twice each frame: once during picking and once during rendering. We only need to
// compute the placePoint, eye distance and extent once per frame, so check the frame timestamp to see if
// this is a new frame. However, we can't use this optimization for 2D continuous globes because the
// Cartesian coordinates of the cube are different for each 2D globe drawn during the current frame.
if (dc.getFrameTimeStamp() != this.frameTimestamp || dc.isContinuous2DGlobe())
{
OrderedCube orderedCube = new OrderedCube();
// Convert the cube's geographic position to a position in Cartesian coordinates. If drawing to a 2D
// globe ignore the shape's altitude.
if (dc.is2DGlobe())
{
orderedCube.placePoint = dc.getGlobe().computePointFromPosition(this.position.getLatitude(),
this.position.getLongitude(), 0);
}
else
{
orderedCube.placePoint = dc.getGlobe().computePointFromPosition(this.position);
}
// Compute the distance from the eye to the cube's position.
orderedCube.eyeDistance = dc.getView().getEyePoint().distanceTo3(orderedCube.placePoint);
// Compute a sphere that encloses the cube. We'll use this sphere for intersection calculations to determine
// if the cube is actually visible.
orderedCube.extent = new Sphere(orderedCube.placePoint, Math.sqrt(3.0) * this.size / 2.0);
// Keep track of the timestamp we used to compute the ordered renderable.
this.frameTimestamp = dc.getFrameTimeStamp();
this.currentFramesOrderedCube = orderedCube;
return orderedCube;
}
else
{
return this.currentFramesOrderedCube;
}
}
/**
* Set up drawing state, and draw the cube. This method is called when the cube is rendered in ordered rendering
* mode.
*
* #param dc Current draw context.
*/
protected void drawOrderedRenderable(DrawContext dc, PickSupport pickCandidates)
{
this.beginDrawing(dc);
try
{
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
if (dc.isPickingMode())
{
Color pickColor = dc.getUniquePickColor();
pickCandidates.addPickableObject(pickColor.getRGB(), this, this.position);
gl.glColor3ub((byte) pickColor.getRed(), (byte) pickColor.getGreen(), (byte) pickColor.getBlue());
}
// Render a unit cube and apply a scaling factor to scale the cube to the appropriate size.
gl.glScaled(this.size, this.size, this.size);
this.drawUnitCube(dc);
}
finally
{
this.endDrawing(dc);
}
}
/**
* Setup drawing state in preparation for drawing the cube. State changed by this method must be restored in
* endDrawing.
*
* #param dc Active draw context.
*/
protected void beginDrawing(DrawContext dc)
{
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
int attrMask = GL2.GL_CURRENT_BIT | GL2.GL_COLOR_BUFFER_BIT;
gl.glPushAttrib(attrMask);
if (!dc.isPickingMode())
{
dc.beginStandardLighting();
gl.glEnable(GL.GL_BLEND);
OGLUtil.applyBlending(gl, false);
// Were applying a scale transform on the modelview matrix, so the normal vectors must be re-normalized
// before lighting is computed.
gl.glEnable(GL2.GL_NORMALIZE);
}
// Multiply the modelview matrix by a surface orientation matrix to set up a local coordinate system with the
// origin at the cube's center position, the Y axis pointing North, the X axis pointing East, and the Z axis
// normal to the globe.
gl.glMatrixMode(GL2.GL_MODELVIEW);
Matrix matrix = dc.getGlobe().computeSurfaceOrientationAtPosition(this.position);
matrix = dc.getView().getModelviewMatrix().multiply(matrix);
double[] matrixArray = new double[16];
matrix.toArray(matrixArray, 0, false);
gl.glLoadMatrixd(matrixArray, 0);
}
/**
* Restore drawing state changed in beginDrawing to the default.
*
* #param dc Active draw context.
*/
protected void endDrawing(DrawContext dc)
{
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
if (!dc.isPickingMode())
dc.endStandardLighting();
gl.glPopAttrib();
}
/**
* Draw a unit cube, using the active modelview matrix to orient the shape.
*
* #param dc Current draw context.
*/
protected void drawUnitCube(DrawContext dc)
{
// Vertices of a unit cube, centered on the origin.
float[][] v = {{-0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, 0.5f}, {0.5f, 0.5f, 0.5f}, {0.5f, 0.5f, -0.5f},
{-0.5f, -0.5f, 0.5f}, {0.5f, -0.5f, 0.5f}, {0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, -0.5f}};
// Array to group vertices into faces
int[][] faces = {{0, 1, 2, 3}, {2, 5, 6, 3}, {1, 4, 5, 2}, {0, 7, 4, 1}, {0, 7, 6, 3}, {4, 7, 6, 5}};
// Normal vectors for each face
float[][] n = {{0, 1, 0}, {1, 0, 0}, {0, 0, 1}, {-1, 0, 0}, {0, 0, -1}, {0, -1, 0}};
// Note: draw the cube in OpenGL immediate mode for simplicity. Real applications should use vertex arrays
// or vertex buffer objects to achieve better performance.
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
gl.glBegin(GL2.GL_QUADS);
try
{
for (int i = 0; i < faces.length; i++)
{
gl.glNormal3f(n[i][0], n[i][1], n[i][2]);
for (int j = 0; j < faces[0].length; j++)
{
gl.glVertex3f(v[faces[i][j]][0], v[faces[i][j]][1], v[faces[i][j]][2]);
}
}
}
finally
{
gl.glEnd();
}
}
protected static class AppFrame extends ApplicationTemplate.AppFrame
{
public AppFrame()
{
super(true, true, false);
RenderableLayer layer = new RenderableLayer();
Cube cube = new Cube(Position.fromDegrees(35.0, -120.0, 3000), 100000);
layer.addRenderable(cube);
getWwd().getModel().getLayers().add(layer);
}
}
public static void main(String[] args)
{
Configuration.setValue(AVKey.INITIAL_LATITUDE, 35.0);
Configuration.setValue(AVKey.INITIAL_LONGITUDE, -120.0);
Configuration.setValue(AVKey.INITIAL_ALTITUDE, 2550000);
Configuration.setValue(AVKey.INITIAL_PITCH, 45);
Configuration.setValue(AVKey.INITIAL_HEADING, 45);
ApplicationTemplate.start("World Wind Custom Renderable Tutorial", AppFrame.class);
}
}
I have reproduced the problem, both with the tutorial Cube class that is included in worldwind.jar and your Cube class that draws a helpfully larger cube.
I have traced the point at which the atmosphere disappears is when the hidden buffer for the GLCanvas is swapped within the WorldWind render code, so the problem is that for some reason the atmosphere and stars layers are not drawn on the hidden buffer during rendering in picking mode using your code.
I then found that the example with Cylinders included as gov.nasa.worldwindx.examples.Cylinders.class draws pickable 3D shapes (cylinders) on the globe and does not exhibit this problem (there are others - Boxes for example is extremely close to this tutorial).
I think that that the issue is with the implementation of OrderedRenderable in this tutorial example - in the Cylinders example, the actual Cylinder class extends RigidShape which in turn extends AbstractShape, which is the class that actually implements OrderedRenderable. It's definitely a bug. Maybe you can work from the Boxes example to get the functionality you need.
I ended up getting an answer on the worldwind forums: http://forum.worldwindcentral.com/showthread.php?46115-Worldwind-Custom-Renderable-Picking-Issue&p=125173
Add a gl push matrix call in the begin drawing method:
protected void beginDrawing(DrawContext dc)
{
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
int attrMask = GL2.GL_CURRENT_BIT | GL2.GL_COLOR_BUFFER_BIT;
gl.glPushAttrib(attrMask);
if (!dc.isPickingMode())
{
dc.beginStandardLighting();
gl.glEnable(GL.GL_BLEND);
OGLUtil.applyBlending(gl, false);
// Were applying a scale transform on the modelview matrix, so the normal vectors must be re-normalized
// before lighting is computed.
gl.glEnable(GL2.GL_NORMALIZE);
}
// Multiply the modelview matrix by a surface orientation matrix to set up a local coordinate system with the
// origin at the cube's center position, the Y axis pointing North, the X axis pointing East, and the Z axis
// normal to the globe.
gl.glPushMatrix();
gl.glMatrixMode(GL2.GL_MODELVIEW);
Matrix matrix = dc.getGlobe().computeSurfaceOrientationAtPosition(this.position);
matrix = dc.getView().getModelviewMatrix().multiply(matrix);
double[] matrixArray = new double[16];
matrix.toArray(matrixArray, 0, false);
gl.glLoadMatrixd(matrixArray, 0);
}
And additionally, add a gl pop matrix call in the end drawing:
protected void endDrawing(DrawContext dc)
{
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
if (!dc.isPickingMode())
dc.endStandardLighting();
gl.glPopMatrix();
gl.glPopAttrib();
}
move
if (!dc.isPickingMode())
dc.endStandardLighting();
from
protected void endDrawing(DrawContext dc)
after
gl.glLoadMatrixd(matrixArray, 0);
in
protected void beginDrawing(DrawContext dc)

Vertex Locations are off when using Mesa Core Profile

I am using Mesa 10.1.3 to be able to use OpenGL 3.3 on my Linux computer. I request a core profile when I create the window since only the core profile has OpenGL 3.3. But when I tried to write a simple program to display a triangle on the screen, I got nothing. So I thought I screwed up somewhere in the code but I rechecked it and it was correct. To test this, I tried running the program in Windows and it was working as it should. So I experimented a little bit more with the code; I multiplied the vertex location in the vertex shader by 0.001 and only then I was able to see my triangle, but even then, it was not working as it should. The triangle I see was a right angle triangle whereas I intended it to be an equilateral one (in Windows I see an equilateral triangle). So my guess is vertex location is somehow different when using OpenGL Core profile, but I don't quite know how to fix this. What am I doing wrong and what should I be doing?
By the way, this is my vertex shader looks like:
#version 330
in vec3 position;
void main()
{
gl_Position = vec4(0.001*position, 1.0);
}
Fragment shader:
#version 330
out vec4 fragColor;
void main()
{
fragColor = vec4(0.0, 1.0, 1.0, 1.0);
}
Shader class:
public Shader()
{
program = glCreateProgram();
if(program == 0)
{
System.err.println("Shader creation failed: Could not find valid memory location");
System.exit(1);
}
}
public void bind()
{
glBindAttribLocation(program, 0, "position");
glUseProgram(program);
}
public void addVertexShader(String text)
{
addProgram(text, GL_VERTEX_SHADER);
}
public void addFragmentShader(String text)
{
addProgram(text, GL_FRAGMENT_SHADER);
}
public void addGeometryShader(String text)
{
addProgram(text, GL_GEOMETRY_SHADER);
}
public void compile()
{
glLinkProgram(program);
if(glGetProgrami(program, GL_LINK_STATUS) == 0)
{
System.err.println(glGetProgramInfoLog(program, 1024));
System.exit(1);
}
glValidateProgram(program);
if(glGetProgrami(program, GL_VALIDATE_STATUS) == 0)
{
System.err.println(glGetProgramInfoLog(program, 1024));
System.exit(1);
}
}
public void addProgram(String text, int type)
{
int shader = glCreateShader(type);
if(shader == 0)
{
System.err.println("Shader creation failed");
System.exit(1);
}
glShaderSource(shader, text);
glCompileShader(shader);
if(glGetShaderi(shader, GL_COMPILE_STATUS) == 0)
{
System.err.println(glGetShaderInfoLog(shader, 1024));
System.exit(1);
}
glAttachShader(program, shader);
}
And my array of vertices which I'm creating the VBO with:
Vertex[] data = new Vertex[] {
new Vertex(new Vector3f(-0.1f, -0.1f, 0)),
new Vertex(new Vector3f(0, 1, 0)),
new Vertex(new Vector3f( 1, -1, 0))};
My draw method:
public void draw()
{
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
//Vertex is a class which only holds a Vector3f for position, currently, so size is set to 3
glVertexAttribPointer(0, 3, GL_FLOAT, false, Vertex.SIZE * 4, 0);
glDrawArrays(GL_TRIANGLES, 0, size);
glDisableVertexAttribArray(0);
}
And this is the result that I'm getting:
Your glBindAttribLocation() call comes too late:
glBindAttribLocation(program, 0, "position");
glUseProgram(program);
glBindAttribLocation() needs to be called before the glLinkProgram() to have any effect. You can move it, or use glGetAttribLocation() after linking to get the location that the linker assigned to the attribute. Or even easier, since you use GLSL 3.30, you can specify the location in the shader code:
layout(location = 0) in vec3 position;
When using the Core Profile, you will also need to use Vertex Array Objects (VAO). If you don't have calls like glGenVertexArrays() and glBindVertexArray() in your code, you will need those. There should be plenty of examples here on SO or on the rest of the internet if you search for "OpenGL VAO" or "OpenGL Vertex Array Object", so I won't repeat too much of it. Roughly, you will have something like this before you start initializing your vertex state:
GLuint vaoId = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// calls to glVertexAttribPointer, glEnableVertexAttribArray
Then when you get ready to draw:
glBindVertexArray(vao);
Your vertex data definition also looks like it could be a source of trouble:
Vertex[] data = new Vertex[] {
new Vertex(new Vector3f(-0.1f, -0.1f, 0)),
new Vertex(new Vector3f(0, 1, 0)),
new Vertex(new Vector3f( 1, -1, 0))};
While the code for filling this into the VBO is not shown, the data passed to glBufferData() needs to be a flat, contiguous array/buffer of floats, while this is an array of vector object references.

Categories

Resources