1

I am trying to understand how shaders work in OpenGL. I want to achieve the following:

INPUT: 
    1 VAO WITH 1 VBO WHICH CONTAINS 1 vec2(x,y) (i.e. just a point defined in vec2)
---->  
VERTEX SHADER:
    JUST TURNS THE vec2(x,y) INTO A vec4(x,y,0.0,1.0)
---->  
GEOMETRY SHADER:
    RECEIVES vec4(x,y,0.0,1.0) POINT AND TRANSLATES INTO 4 POINTS
    vec4(x-0.2,y-0.2,0.0,1.0) 
    vec4(x-0.2,y+0.2,0.0,1.0) 
    vec4(x+0.2,y+0.2,0.0,1.0) 
    vec4(x+0.2,y-0.2,0.0,1.0) 
---->  
FRAGMENT SHADER:
    DRAWS EACH ONE OF THE 4 PIXELS IN BLUE

So basically I pass a pair of x,y coordinates in a vec2 to the vertex shader and I expect to get 4 points drawn around that point (separated 0.2 in both coordinates from that point). It is like drawing 4 points of a square when you pass it just the center of that imaginary square.

This is the code I have tried:


Vertex shader:

#version 330 core

layout (location = 0) in vec2 squareCenterPosition;

void main()
{
    gl_Position = vec4(squareCenterPosition.x, squareCenterPosition.y, 0.0, 1.0);
}

Geometry shader:

#version 330 core

layout (points) in;
layout (points, max_vertices = 1) out;

void main() {  
    vec4 gl_Position;

    gl_Position = gl_in[0].gl_Position + vec4(-0.2, -0.2, 0.0, 0.0); 
    EmitVertex();
    EndPrimitive();

    gl_Position = gl_in[0].gl_Position + vec4(-0.2, +0.2, 0.0, 0.0);
    EmitVertex();
    EndPrimitive();

    gl_Position = gl_in[0].gl_Position + vec4(+0.2, +0.2, 0.0, 0.0); 
    EmitVertex();
    EndPrimitive();

    gl_Position = gl_in[0].gl_Position + vec4(+0.2, -0.2, 0.0, 0.0);
    EmitVertex();    
    EndPrimitive();
}  

Fragment shader:

#version 330 core

in vec4 vertexColor;
out vec4 color;

void main(void) {
     color = vec4(1.0, 0.0, 0.0, 1.0);
}

JOGL class implementing this:

package openglexample1;

import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GL3;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.glu.GLU;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Vector;
import java.util.stream.Stream;
import javax.swing.JFrame;

public class WindowC extends JFrame implements GLEventListener {

    /**
     * @return the glcanvas
     */
    public GLCanvas getGlcanvas() {
        return glcanvas;
    }

    //getting the capabilities object of GL2 profile
    final private GLProfile profile;
    final private GLCapabilities capabilities;
    final private GLCanvas glcanvas;

    // GL PROGAM
    private int glProgram;

    // VAO AND VBOs
    private int vao[] = new int[1];    // VAO GROUPS VBOs, ONLY ONE USED
    private int vbo[] = new int[2];    // 2 VBOs FOR 2 SQUARES

    public WindowC() {

        // OpenGL CAPABILITIES
        profile = GLProfile.get(GLProfile.GL2);
        capabilities = new GLCapabilities(profile);

        // CANVAS
        glcanvas = new GLCanvas(capabilities);
        glcanvas.addGLEventListener(this);
        glcanvas.setSize(400, 400);

        // JFRAME
        this.getContentPane().add(glcanvas);
        this.setSize(this.getContentPane().getPreferredSize());

    }

    @Override
    public void display(GLAutoDrawable drawable) {

        GL2 gl = drawable.getGL().getGL2();

        // USE PROGRAM
        gl.glUseProgram(glProgram);

        // USE VBOs
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo[0]); 
        gl.glVertexAttribPointer(0, 2, gl.GL_FLOAT, false, 0, 0); // associate 0th vertex attribute with active buffer
        gl.glEnableVertexAttribArray(0); // enable the 0th vertex attribute

        // DRAW POINT
        gl.glDrawArrays(GL2.GL_POINTS, 0, 1);


    }

    @Override
    public void init(GLAutoDrawable drawable) {

        GL2 gl = drawable.getGL().getGL2();

        // CREATE PROGRAM
        glProgram = gl.glCreateProgram();

        // CREATE VERTEX SHADER
        int vertexShader = gl.glCreateShader(GL2.GL_VERTEX_SHADER);
        int geometryShader = gl.glCreateShader(GL2.GL_GEOMETRY_SHADER_BIT);
        int fragmentShader = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER);

        // LOAD SOURCE CODE
        String[] vertexShaderSource = readShader("VertexShader1.glsl");
        gl.glShaderSource(vertexShader, vertexShaderSource.length, vertexShaderSource, null, 0); 
        gl.glCompileShader(vertexShader);

        String[] geometryShaderSource = readShader("GeometryShader1.glsl");
        gl.glShaderSource(geometryShader, geometryShaderSource.length, geometryShaderSource, null, 0); 
        gl.glCompileShader(geometryShader);

        String[] fragmentShaderSource = readShader("FragmentShader1.glsl");
        gl.glShaderSource(fragmentShader, fragmentShaderSource.length, fragmentShaderSource, null, 0); 
        gl.glCompileShader(fragmentShader);

        // ATTACH VERTEX SHADER TO PROGRAM, LINK AND DELETE SHADERS
        gl.glAttachShader(glProgram, vertexShader);
        gl.glAttachShader(glProgram, geometryShader);
        gl.glAttachShader(glProgram, fragmentShader);
        gl.glLinkProgram(glProgram);
        gl.glDeleteShader(vertexShader);
        gl.glDeleteShader(geometryShader);
        gl.glDeleteShader(fragmentShader);

        // CREATE VAO
        gl.glGenVertexArrays(1, vao, 0);
        gl.glBindVertexArray(vao[0]);

        // COORDINATES SQUARES
        float[] coordinatesSquare1 = new float[]{0.5f, 0.5f};

        // CREATE VBOs
        gl.glGenBuffers(1, vbo, 0);

        // POPULATE VBO 1 FOR SQUARE 1
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo[0]);
        FloatBuffer bufferSquare1 = Buffers.newDirectFloatBuffer(coordinatesSquare1);
        gl.glBufferData(gl.GL_ARRAY_BUFFER, bufferSquare1.limit()*4, bufferSquare1, gl.GL_STATIC_DRAW);            

    }

    @Override
    public void dispose(GLAutoDrawable drawable) {
    }

    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        System.out.println("reshape");
        this.display(drawable);

    }

    private String[] readShader(String filename) {  

        Vector<String> lines = new Vector<String>();

        try (Stream<String> stream = Files.lines(Paths.get(filename))) {
            stream.forEach(x -> lines.add(x));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // CONVERT VECTOR TO ARRAY
        Object[] objArray = lines.toArray();
        String[] array = Arrays.copyOf(objArray, objArray.length, String[].class);         

        return array;
    } 

}

Java "main" class invoking the JFrame with the GLCanvas:

package openglexample1;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.WindowConstants;

public class OpenGLExample2 {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

      // THIRD WINDOW
      WindowC basicFrame3 = new WindowC();
      basicFrame3.setTitle("WindowC");        
      basicFrame3.setLocation(400,400);
      basicFrame3.setVisible(true);
      basicFrame3.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    }

}

But I am getting just a white pixel (not blue) at (0.5,0.5) coordinates instead of the four blue pixels. Any tip on how could I further debug this is welcomed.

enter image description here

---


EDIT:

Following below answer I edit code as follows. Uses GL3 only and implements changes stated in the answer, however the result is still the same.

Java OpenGL class:

package openglexample1;

import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.GL3;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.glu.GLU;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Vector;
import java.util.stream.Stream;
import javax.swing.JFrame;

public class WindowC extends JFrame implements GLEventListener {

    /**
     * @return the glcanvas
     */
    public GLCanvas getGlcanvas() {
        return glcanvas;
    }

    //getting the capabilities object of GL2 profile
    final private GLProfile profile;
    final private GLCapabilities capabilities;
    final private GLCanvas glcanvas;

    // GL PROGAM
    private int glProgram;

    // VAO AND VBOs
    private int vao[] = new int[1];    // VAO GROUPS VBOs, ONLY ONE USED
    private int vbo[] = new int[2];    // 2 VBOs FOR 2 SQUARES

    public WindowC() {

        // OpenGL CAPABILITIES
        profile = GLProfile.get(GLProfile.GL3);
        capabilities = new GLCapabilities(profile);

        // CANVAS
        glcanvas = new GLCanvas(capabilities);
        glcanvas.addGLEventListener(this);
        glcanvas.setSize(400, 400);

        // JFRAME
        this.getContentPane().add(glcanvas);
        this.setSize(this.getContentPane().getPreferredSize());

    }

    @Override
    public void display(GLAutoDrawable drawable) {

        GL3 gl = drawable.getGL().getGL3();

        // USE PROGRAM
        gl.glUseProgram(glProgram);

        // USE VBOs
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo[0]); 
        gl.glVertexAttribPointer(0, 2, gl.GL_FLOAT, false, 0, 0); // associate 0th vertex attribute with active buffer
        gl.glEnableVertexAttribArray(0); // enable the 0th vertex attribute

        // DRAW POINT
        gl.glDrawArrays(GL3.GL_POINTS, 0, 1);


    }

    @Override
    public void init(GLAutoDrawable drawable) {

        GL3 gl = drawable.getGL().getGL3();

        // CREATE PROGRAM
        glProgram = gl.glCreateProgram();

        // CREATE VERTEX SHADER
        int vertexShader = gl.glCreateShader(GL3.GL_VERTEX_SHADER);
        int geometryShader = gl.glCreateShader(GL3.GL_GEOMETRY_SHADER);
        int fragmentShader = gl.glCreateShader(GL3.GL_FRAGMENT_SHADER);

        // LOAD SOURCE CODE
        String[] vertexShaderSource = readShader("VertexShader1.glsl");
        gl.glShaderSource(vertexShader, vertexShaderSource.length, vertexShaderSource, null, 0); 
        gl.glCompileShader(vertexShader);

        String[] geometryShaderSource = readShader("GeometryShader1.glsl");
        gl.glShaderSource(geometryShader, geometryShaderSource.length, geometryShaderSource, null, 0); 
        gl.glCompileShader(geometryShader);

        String[] fragmentShaderSource = readShader("FragmentShader1.glsl");
        gl.glShaderSource(fragmentShader, fragmentShaderSource.length, fragmentShaderSource, null, 0); 
        gl.glCompileShader(fragmentShader);

        // ATTACH VERTEX SHADER TO PROGRAM, LINK AND DELETE SHADERS
        gl.glAttachShader(glProgram, vertexShader);
        gl.glAttachShader(glProgram, geometryShader);
        gl.glAttachShader(glProgram, fragmentShader);
        gl.glLinkProgram(glProgram);
        gl.glDeleteShader(vertexShader);
        gl.glDeleteShader(geometryShader);
        gl.glDeleteShader(fragmentShader);

        // CREATE VAO
        gl.glGenVertexArrays(1, vao, 0);
        gl.glBindVertexArray(vao[0]);

        // COORDINATES SQUARES
        float[] coordinatesSquare1 = new float[]{0.5f, 0.5f};

        // CREATE VBOs
        gl.glGenBuffers(1, vbo, 0);

        // POPULATE VBO 1 FOR SQUARE 1
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo[0]);
        FloatBuffer bufferSquare1 = Buffers.newDirectFloatBuffer(coordinatesSquare1);
        gl.glBufferData(gl.GL_ARRAY_BUFFER, bufferSquare1.limit()*4, bufferSquare1, gl.GL_STATIC_DRAW);            

    }

    @Override
    public void dispose(GLAutoDrawable drawable) {
    }

    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        System.out.println("reshape");
        this.display(drawable);

    }

    private String[] readShader(String filename) {  

        Vector<String> lines = new Vector<String>();

        try (Stream<String> stream = Files.lines(Paths.get(filename))) {
            stream.forEach(x -> lines.add(x));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // CONVERT VECTOR TO ARRAY
        Object[] objArray = lines.toArray();
        String[] array = Arrays.copyOf(objArray, objArray.length, String[].class);         

        return array;
    } 

}

Geometry Shader:

#version 330 core

layout (points) in;
layout (points, max_vertices = 4) out;

void main() {  

    gl_Position = gl_in[0].gl_Position + vec4(-0.2, -0.2, 0.0, 0.0); 
    EmitVertex();    
    EndPrimitive();

    gl_Position = gl_in[0].gl_Position + vec4(-0.2, +0.2, 0.0, 0.0);
    EmitVertex();    
    EndPrimitive();

    gl_Position = gl_in[0].gl_Position + vec4(+0.2, +0.2, 0.0, 0.0); 
    EmitVertex();    
    EndPrimitive();

    gl_Position = gl_in[0].gl_Position + vec4(+0.2, -0.2, 0.0, 0.0);
    EmitVertex();    
    EndPrimitive();
}  
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
M.E.
  • 4,955
  • 4
  • 49
  • 128
  • @rabbid76 a vec2 input variable representing (x,y) position. the vertex shader is intended to construct a regular vec4(x,y,0.0,1.0) from that vec2 – M.E. Feb 06 '20 at 11:23
  • Deleted too, I have edited the question to reflect all changes done so far. Unfortuantelly still get a single white point at 0.5,0.5. Could it be that shaders are not being executed at all? – M.E. Feb 06 '20 at 12:09
  • How/where do you check for errors? I would expect to see calls to something like `GL3.glGetError()` -- or are things done differently in java? – G.M. Feb 06 '20 at 12:17
  • As this is my first OpenGL progam, I have no idea on how to see errors, but I will search how to do that and try to implement that into the code. – M.E. Feb 06 '20 at 12:18
  • -------> `max_vertices = 1` <------- ? – user253751 Feb 06 '20 at 12:24
  • @user253751 See the bottom part of the question (edit) and my answer! – Rabbid76 Feb 06 '20 at 12:27
  • @user253751 I have preserved the original question and added at the end of the question the "final" code with the changes suggested by Rabbid76 (which clearly address actual OpenGL problems in the code), that includes modifying max_vertices=4. Unfortunatelly there is something else still missing, I will try to get some error log/output. – M.E. Feb 06 '20 at 12:30

1 Answers1

3

There are some issue

You have declared a local variable vec4 gl_Position; in the geometry shader. That should cause a compile error, since all names starting with gl_ are reserved for the implementation. Anyway the geometry shader would never write to the per vertex output gl_Position. See Geometry Shader- Outputs.
Delete the declaration:

vec4 gl_Position;


The specification of the maximum vertices is:

layout (points, max_vertices = 1) out;

max_vertices is a compile time constant and defines the maximum number of vertices that will be written by a single invocation. See Geometry Shader.

int geometryShader = gl.glCreateShader(GL2.GL_GEOMETRY_SHADER_BIT);

The number of maximum vertices has to be 4:

layout (points, max_vertices = 4) out;

The shader type has to be GL32.GL_GEOMETRY_SHADER rather than GL2.GL_GEOMETRY_SHADER_BIT:

int geometryShader = gl.glCreateShader(GL2.GL_GEOMETRY_SHADER_BIT);

int geometryShader = gl.glCreateShader(GL32.GL_GEOMETRY_SHADER);

GL_GEOMETRY_SHADER_BIT is not a valid parameter for glCreateShader and will cause an INVALID_ENUM error.


For the use of a Geometry Shader you have to create a OpenGL 3.2 Context (at least):
(See Class GLProfile)

profile = GLProfile.get(GLProfile.GL2);

profile = GLProfile.get(GLProfile.GL3);
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 1
    "*That causes that the geometry shader never writes to the per vertex output gl_Position*" It *should* cause a compile error, since all names starting with `gl_` are reserved for the implementation, and the `gl_Position` variable in geometry shaders is part of an interface block, so this doesn't count as redeclaring it properly. – Nicol Bolas Feb 06 '20 at 14:28
  • Implemented all changes (see edited question) but still same behaviour, I will try to get compile output for the shaders to see if there is another mistake somewhere else – M.E. Feb 06 '20 at 15:21
  • Related question, seems that the vertex shader is NOT compiling, which explains why the other two shaders are doing nothing. https://stackoverflow.com/questions/60098830/how-do-i-get-more-information-on-why-this-simple-vertex-shader-compilation-is-fa – M.E. Feb 06 '20 at 15:50
  • 1
    It works, but it also needs to implement the fix for the ```readShader``` routine in the above related question. The last error was that code was being submitted without "\n". Apart from that this answer fixes all OpenGL issues. – M.E. Feb 06 '20 at 16:43