/* OpenGL example code - Geometry Shader and Blending * * Uses a geometry shader to expand points to billboard quads. * The billboards are then blended while drawing to create a galaxy * made of particles. * * Autor: Jakob Progsch */ #include #include //glm is used to create perspective and transform matrices #include #include #include #include #include #include #include // helper to check and display for shader compiler errors bool check_shader_compile_status(GLuint obj) { GLint status; glGetShaderiv(obj, GL_COMPILE_STATUS, &status); if(status == GL_FALSE) { GLint length; glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &length); std::vector log(length); glGetShaderInfoLog(obj, length, &length, &log[0]); std::cerr << &log[0]; return false; } return true; } // helper to check and display for shader linker error bool check_program_link_status(GLuint obj) { GLint status; glGetProgramiv(obj, GL_LINK_STATUS, &status); if(status == GL_FALSE) { GLint length; glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &length); std::vector log(length); glGetProgramInfoLog(obj, length, &length, &log[0]); std::cerr << &log[0]; return false; } return true; } int main() { int width = 640; int height = 480; if(glfwInit() == GL_FALSE) { std::cerr << "failed to init GLFW" << std::endl; return 1; } // select opengl version glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // create a window GLFWwindow *window; if((window = glfwCreateWindow(width, height, "07geometry_shader_blending", 0, 0)) == 0) { std::cerr << "failed to open window" << std::endl; glfwTerminate(); return 1; } glfwMakeContextCurrent(window); if(glxwInit()) { std::cerr << "failed to init GL3W" << std::endl; glfwDestroyWindow(window); glfwTerminate(); return 1; } // the vertex shader simply passes through data std::string vertex_source = "#version 330\n" "layout(location = 0) in vec4 vposition;\n" "void main() {\n" " gl_Position = vposition;\n" "}\n"; // the geometry shader creates the billboard quads std::string geometry_source = "#version 330\n" "uniform mat4 View;\n" "uniform mat4 Projection;\n" "layout (points) in;\n" "layout (triangle_strip, max_vertices = 4) out;\n" "out vec2 txcoord;\n" "void main() {\n" " vec4 pos = View*gl_in[0].gl_Position;\n" " txcoord = vec2(-1,-1);\n" " gl_Position = Projection*(pos+vec4(txcoord,0,0));\n" " EmitVertex();\n" " txcoord = vec2( 1,-1);\n" " gl_Position = Projection*(pos+vec4(txcoord,0,0));\n" " EmitVertex();\n" " txcoord = vec2(-1, 1);\n" " gl_Position = Projection*(pos+vec4(txcoord,0,0));\n" " EmitVertex();\n" " txcoord = vec2( 1, 1);\n" " gl_Position = Projection*(pos+vec4(txcoord,0,0));\n" " EmitVertex();\n" "}\n"; // the fragment shader creates a bell like radial color distribution std::string fragment_source = "#version 330\n" "in vec2 txcoord;\n" "layout(location = 0) out vec4 FragColor;\n" "void main() {\n" " float s = 0.2*(1/(1+15.*dot(txcoord, txcoord))-1/16.);\n" " FragColor = s*vec4(1,0.9,0.6,1);\n" "}\n"; // program and shader handles GLuint shader_program, vertex_shader, geometry_shader, fragment_shader; // we need these to properly pass the strings const char *source; int length; // create and compiler vertex shader vertex_shader = glCreateShader(GL_VERTEX_SHADER); source = vertex_source.c_str(); length = vertex_source.size(); glShaderSource(vertex_shader, 1, &source, &length); glCompileShader(vertex_shader); if(!check_shader_compile_status(vertex_shader)) { glfwDestroyWindow(window); glfwTerminate(); return 1; } // create and compiler geometry shader geometry_shader = glCreateShader(GL_GEOMETRY_SHADER); source = geometry_source.c_str(); length = geometry_source.size(); glShaderSource(geometry_shader, 1, &source, &length); glCompileShader(geometry_shader); if(!check_shader_compile_status(geometry_shader)) { glfwDestroyWindow(window); glfwTerminate(); return 1; } // create and compiler fragment shader fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); source = fragment_source.c_str(); length = fragment_source.size(); glShaderSource(fragment_shader, 1, &source, &length); glCompileShader(fragment_shader); if(!check_shader_compile_status(fragment_shader)) { glfwDestroyWindow(window); glfwTerminate(); return 1; } // create program shader_program = glCreateProgram(); // attach shaders glAttachShader(shader_program, vertex_shader); glAttachShader(shader_program, geometry_shader); glAttachShader(shader_program, fragment_shader); // link the program and check for errors glLinkProgram(shader_program); check_program_link_status(shader_program); // obtain location of projection uniform GLint View_location = glGetUniformLocation(shader_program, "View"); GLint Projection_location = glGetUniformLocation(shader_program, "Projection"); // vao and vbo handle GLuint vao, vbo; // generate and bind the vao glGenVertexArrays(1, &vao); glBindVertexArray(vao); // generate and bind the vertex buffer object glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); const int particles = 128*1024; // create a galaxylike distribution of points std::vector vertexData(particles*3); for(int i = 0;i