Почему геометрический шейдер не генерирует 4 точки из 1 точки, как предполагалось? - PullRequest
1 голос
/ 06 февраля 2020

Я пытаюсь понять, как работают шейдеры в OpenGL. Я хочу добиться следующего:

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

Таким образом, в основном, я передаю пару координат x, y в vec2 в вершинный шейдер, и я ожидаю получить 4 точки, нарисованные вокруг этой точки (разделенные 0.2 в обоих координаты от этой точки). Это все равно что рисовать 4 точки квадрата, когда вы проходите через центр воображаемого квадрата.

Вот код, который я пробовал:


Вершинный шейдер:

#version 330 core

layout (location = 0) in vec2 squareCenterPosition;

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

Геометрия шейдера:

#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();
}  

Фрагмент шейдера:

#version 330 core

in vec4 vertexColor;
out vec4 color;

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

Класс JOGL, реализующий это:

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 "основной" класс, вызывающий JFrame с 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);

    }

}

Но я получаю только белый пиксель (не синий) с координатами (0,5,0,5) вместо четырех синих пикселей. Любой совет о том, как я могу далее отладить это приветствуется.

enter image description here

---


РЕДАКТИРОВАТЬ:

После ответа ниже я редактирую код следующим образом. Использует только GL3 и реализует изменения, указанные в ответе, однако результат остается тем же.

Java Класс OpenGL:

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;
    } 

}

Геометрический шейдер:

#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();
}  

1 Ответ

3 голосов
/ 06 февраля 2020

Есть некоторые проблемы

Вы объявили локальную переменную vec4 gl_Position; в геометрическом шейдере. Это должно вызвать ошибку компиляции, поскольку все имена, начинающиеся с gl_, зарезервированы для реализации. В любом случае геометрический шейдер никогда не будет записывать в вывод каждой вершины gl_Position. См. Geometry Shader - выходы .
Удалить объявление:

vec4 gl_Position;


Спецификация максимальных вершин: :

layout (points, max_vertices = 1) out;

max_vertices - это константа времени компиляции, которая определяет максимальное количество вершин, которые будут записаны одним вызовом. См. Геометрический шейдер .

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

Число максимальных вершин должно быть 4:

layout (points, max_vertices = 4) out;

Тип шейдера должен быть GL32.GL_GEOMETRY_SHADER вместо 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 не является допустимым параметром для glCreateShader и будет вызвать ошибку INVALID_ENUM.


Для использования Geometry Shader необходимо создать контекст OpenGL 3.2 (как минимум):
(см. Класс GLProfile)

profile = GLProfile.get(GLProfile.GL2);

profile = GLProfile.get(GLProfile.GL3);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...