I recently started learning OpenGL and I have been trying to write a program that displays a diamond on the screen using VAOs and VBOs with shaders. My code is mostly based on this tutorial: https://www.opengl.org/wiki/Tutorial2:_VAOs,_VBOs,_Vertex_and_Fragment_Shaders_%28C_/_SDL%29 and I also used the shaders from the tutorial. The diamond is supposed to be drawn using the color information from the vertex array object but instead it is just drawn in white. The shaders seem to be loading fine so I think it is a problem with my vertex and buffer array objects. Can someone please explain why my code is not working how I expected or show a clear example of how to render VAOs using colors from an array of color attributes.
package windows;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
import shaders.ShaderControl2;
import com.jogamp.common.nio.Buffers;
public class Test4 implements GLEventListener{
ShaderControl2 sc;
FloatBuffer vertexPos, vertexCol;
IntBuffer vao, vbo;
GLU glu = new GLU();
public static void main(String[] args){
GLProfile glp = GLProfile.getDefault();
GLCapabilities caps = new GLCapabilities(glp);
GLCanvas canvas = new GLCanvas(caps);
Test4 t = new Test4();
canvas.addGLEventListener(t);
Frame f = new Frame("TEST #4");
f.setSize(400,400);
f.add(canvas);
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
@Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
gl.glClearColor(0, 0, 0, 1f);
gl.glClear(GL2.GL_COLOR_BUFFER_BIT);
sc.useShader(gl);
gl.glBindVertexArray(vao.get(0));
gl.glEnableVertexAttribArray(0);
gl.glEnableVertexAttribArray(1);
gl.glDrawArrays(GL2.GL_LINE_LOOP, 0 , 4);
sc.dontUseShader(gl);
}
@Override
public void dispose(GLAutoDrawable drawable) {
// TODO Auto-generated method stub
}
@Override
public void init(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
System.out.println(gl.glGetString(GL2.GL_VERSION));
vertexPos = Buffers.newDirectFloatBuffer(8);
vertexPos.put(new float[]{0f, 1f});
vertexPos.put(new float[]{1f, 0f});
vertexPos.put(new float[]{0f, -1f});
vertexPos.put(new float[]{-1f, 0});
vertexPos.flip();
vertexCol = Buffers.newDirectFloatBuffer(12);
vertexCol.put(new float[]{1f, 0f, 0f});
vertexCol.put(new float[]{0f, 1f, 0f});
vertexCol.put(new float[]{0f, 0f, 1f});
vertexCol.put(new float[]{1f, 1f, 1f});
vertexCol.flip();
vao = IntBuffer.allocate(1);
vbo = IntBuffer.allocate(2);
gl.glGenVertexArrays(1, vao);
gl.glGenBuffers(2, vbo);
int bytesPerFloat = Float.SIZE/Byte.SIZE;
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vbo.get(0));
gl.glBufferData(GL2.GL_ARRAY_BUFFER, vertexPos.capacity() * bytesPerFloat, vertexPos, GL2.GL_STATIC_DRAW);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vbo.get(1));
gl.glBufferData(GL2.GL_ARRAY_BUFFER, vertexCol.capacity() * bytesPerFloat, vertexCol, GL2.GL_STATIC_DRAW);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
gl.glBindVertexArray(vao.get());
gl.glEnableVertexAttribArray(0);
gl.glEnableVertexAttribArray(1);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vbo.get(0));
gl.glVertexAttribPointer(0, 2, GL2.GL_FLOAT, false, 0, 0);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vbo.get(1));
gl.glVertexAttribPointer(1, 3, GL2.GL_FLOAT, false, 0, 0);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
sc = new ShaderControl2();
sc.vSrc = sc.loadShader("v.txt");
sc.fSrc = sc.loadShader("f.txt");
}
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width,
int height) {
}
}
ShaderControl2 code:
package shaders;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import javax.media.opengl.*;
public class ShaderControl2 {
private int vertexShaderProg, fragmentShaderProg, shaderProg;
public String[] vSrc, fSrc;
public String[] loadShader(String sFile){
String line = new String();
StringBuilder fileContent = new StringBuilder();
try{
InputStream is = getClass().getResourceAsStream(sFile);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while((line = br.readLine()) != null){
fileContent.append(line + "\n");
}
is.close();
} catch(Exception e){
e.printStackTrace();
}
System.out.println("Shader file content:\n" + fileContent);
return new String[]{fileContent.toString()};
}
public void attachShader(GL2 gl){
IntBuffer isCompiledVS = IntBuffer.allocate(1), isCompiledFS = IntBuffer.allocate(1), isLinked = IntBuffer.allocate(1);
IntBuffer vLogLength = IntBuffer.allocate(1), fLogLength = IntBuffer.allocate(1), linkLogLength = IntBuffer.allocate(1);
ByteBuffer vertexInfoLog, fragmentInfoLog, linkInfoLog;
int size;
vertexShaderProg = gl.glCreateShader(GL2.GL_VERTEX_SHADER);
gl.glShaderSource(vertexShaderProg, 1, vSrc, null);
gl.glCompileShader(vertexShaderProg);
gl.glGetShaderiv(vertexShaderProg, GL2.GL_COMPILE_STATUS, isCompiledVS);
if(isCompiledVS.get(0) == 0){
System.out.println("Failed to compile vertexShaderProg");
gl.glGetShaderiv(vertexShaderProg, GL2.GL_INFO_LOG_LENGTH, vLogLength);
size = vLogLength.get(0);
vertexInfoLog = ByteBuffer.allocate(size);
gl.glGetShaderInfoLog(vertexShaderProg, size, vLogLength, vertexInfoLog);
for(byte b : vertexInfoLog.array()){
System.err.print((char)b);
}
}
fragmentShaderProg = gl.glCreateShader(GL2.GL_VERTEX_SHADER);
gl.glShaderSource(fragmentShaderProg, 1, vSrc, null);
gl.glCompileShader(fragmentShaderProg);
gl.glGetShaderiv(fragmentShaderProg, GL2.GL_COMPILE_STATUS, isCompiledFS);
if(isCompiledFS.get(0) == 0){
System.out.println("Failed to compile fragmentShaderProg");
gl.glGetShaderiv(fragmentShaderProg, GL2.GL_INFO_LOG_LENGTH, fLogLength);
size = fLogLength.get(0);
fragmentInfoLog = ByteBuffer.allocate(size);
gl.glGetShaderInfoLog(fragmentShaderProg, size, fLogLength, fragmentInfoLog);
for(byte b : fragmentInfoLog.array()){
System.err.print((char)b);
}
}
shaderProg = gl.glCreateProgram();
gl.glAttachShader(shaderProg, vertexShaderProg);
gl.glAttachShader(shaderProg, fragmentShaderProg);
gl.glBindAttribLocation(shaderProg, 0, "in_Position");
gl.glBindAttribLocation(shaderProg, 1, "in_Color");
gl.glLinkProgram(shaderProg);
gl.glGetProgramiv(shaderProg, GL2.GL_LINK_STATUS, isLinked);
if(isLinked.get(0) == 0){
System.out.println("Failed to link shaderProg");
gl.glGetShaderiv(shaderProg, GL2.GL_INFO_LOG_LENGTH, linkLogLength);
size = linkLogLength.get(0);
linkInfoLog = ByteBuffer.allocate(size);
gl.glGetProgramInfoLog(shaderProg, size, linkLogLength, linkInfoLog);
for(byte b : linkInfoLog.array()){
System.err.print((char)b);
}
}
}
public int useShader(GL2 gl){
gl.glUseProgram(shaderProg);
return shaderProg;
}
public void dontUseShader(GL2 gl){
gl.glUseProgram(0);
}
}
Shader Code
f.txt:
#version 210
// It was expressed that some drivers required this next line to function properly
precision highp float;
in vec3 ex_Color;
out vec4 gl_FragColor;
void main(void) {
// Pass through our original color with full opacity.
gl_FragColor = vec4(ex_Color,1.0);
}
v.txt:
#version 210
// in_Position was bound to attribute index 0 and in_Color was bound to attribute index 1
in vec2 in_Position;
in vec3 in_Color;
// We output the ex_Color variable to the next shader in the chain
out vec3 ex_Color;
void main(void) {
// Since we are using flat lines, our input only had two points: x and y.
// Set the Z coordinate to 0 and W coordinate to 1
gl_Position = vec4(in_Position.x, in_Position.y, 0.0, 1.0);
// GLSL allows shorthand use of vectors too, the following is also valid:
// gl_Position = vec4(in_Position, 0.0, 1.0);
// We're simply passing the color through unmodified
ex_Color = in_Color;
}
The reason you see just white lines is because the shader program is not being used at all.
I can't see where you call attach shaders (you never call sc.attachShader(gl), I added sc.attachShader(gl) at the end of the init method.
Now a lot of errors will appear.
In attachshader, when you create fragmentShaderProg you create it as a GL_VERTEX_SHADER, it should be GL_FRAGMENT_SHADER, and just below that you are sending the source of the vertex shader in place of the fragment shader.
version 210 of the GLSL language doesn't exist, change it to version 130. The thing with the GLSL version is that it is independent from the OpenGL version, https://github.com/mattdesl/lwjgl-basics/wiki/GLSL-Versions
#version 130
Now add to the end of the init method
sc.attachShader(gl)
and change
fragmentShaderProg = gl.glCreateShader(GL2.GL_VERTEX_SHADER);
gl.glShaderSource(fragmentShaderProg, 1, vSrc, null);
for
fragmentShaderProg = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER);
gl.glShaderSource(fragmentShaderProg, 1, fSrc, null);
now it works.