Briefly
Point sprites make OpenGL points rendered using a fragment shader. When running, the fragment coordinates in the point need to be considered. This coordinate is saved by the built-in two-dimensional vector gl_PointCoord. The most common function of this variable is to use it as texture coordinates or to calculate color and coverage.
Apply a texture to a point sprite
Points drawn in OpenGL have only one position coordinate. If you want to attach a texture to a point, you need to use the built-in variable gl_PointCoord to query the texels in the texture, so that a textured point sprite can be generated. Environment : OPenGL3.3, glfw, glew.
Simple point sprite sample source code
shader
// vertex shader #version 330 core layout (location = 0) in vec3 aPos; void main() { gl_Position = vec4(aPos.x, aPos.y, 0, 1.0); //Set the size of the point sprite gl_PointSize = 80; };
// fragment shader
#version
330 core
uniform sampler2D sprite_texture;
out vec4 FragColor;
void main()
{
FragColor = texture(sprite_texture, gl_PointCoord);
};
application
In the application, a texture object needs to be generated and associated to the shader, which is the same as ordinary texture mapping. Enable point sprite-related functions before drawing, and the drawing type must be GL_POINTS.
Note : There are two ways to draw the size of the point sprite. One is to call the function glPointSize() in the application to set the size of the point. In this way, the size of the point is fixed and will not become smaller with the distance from the camera. . Another is to set it in the vertex shader, using the built-in output variable two gl_PointSize to set, which can perform a linear mapping according to their distance from the near plane.
#define GLFW_STATIC void framebuffer_size_callback(GLFWwindow* window, int width, int height); void processInput(GLFWwindow *window); const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600; intmain() { //--------------------------------------------------------------------------------- glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", NULL, NULL); if (window == NULL) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) { std::cout << "Failed to initialize GLEW" << std::endl; return -1; } glViewport(0, 0, 800, 600); //--------------------------------------------------------------------------------- Shader ourShader("./shader/Points.vs", "./shader/Points.fs"); float vertices[] = { // position // color 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // Bottom right -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // Bottom left 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // top }; //---------------Texture 1 --------------- unsigned int texture1; // create texture glGenTextures(1, &texture1); //bind texture glBindTexture(GL_TEXTURE_2D, texture1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //The width, height and number of color channels of the image int width, height, nrChannels; stbi_set_flip_vertically_on_load(true); // load texture unsigned char *data = stbi_load("bricks2.jpg", &width, &height, &nrChannels, 0); //if the load was successful if (data) { // Generate a texture using the loaded image data glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); } else { std::cout << "Failed to load texture" << std::endl; } // release image memory stbi_image_free(data); //create cache object unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); //Bind VAO glBindVertexArray(VAO); //Bind VBO glBindBuffer(GL_ARRAY_BUFFER, VBO); //copy vertex array to VBO glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // position property, the first parameter is the shader position glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // color property glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); while (!glfwWindowShouldClose(window)) { processInput(window); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // point sprite settings glEnable(GL_POINT_SPRITE); glEnable(GL_PROGRAM_POINT_SIZE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); // activate the shader ourShader.use(); glBindTexture(GL_TEXTURE_2D, texture1); //Bind the vertex buffer object glBindVertexArray(VAO); // start drawing glDrawArrays(GL_POINTS, 0, 3); //unbind glBindVertexArray(0); // swap cache glfwSwapBuffers(window); //Query io events glfwPollEvents(); } // delete vertex array object and vertex buffer object glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glfwTerminate(); return 0; } void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } void processInput(GLFWwindow *window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } }
Effect
Parse point sprite color and shape
The resolution of textures is limited and we are not limited to just loading data from textures. gl_PointCoord is very accurate. Take gl_PointCoord as the center of the point, and then calculate the distance between the fragment and the center of the point sprite. If it is greater than 0.25, use the discard keyword to reject the fragment, thus generating a circle with a radius of 0.25.
Sample source code
Note: Only need to modify the shader code, the application is the same as above
// vertex shader #version 330 core layout (location = 0) in vec3 aPos; void main() { gl_PointSize = 80; gl_Position = vec4(aPos.x, aPos.y, 0, 1.0); };
// fragment shader #version 330 core uniform sampler2D sprite_texture; out vec4 FragColor; void main() { const vec4 color1 = vec4(0.6,0.0,0.0,1.0); const vec4 color2 = vec4(0.9,0.7,1.0,0.0); vec2 temp = gl_PointCoord - vec2(0.5); float f = dot(temp, temp); if(f > 0.25) { discard; } FragColor = mix(color1, color2, smoothstep(0.1, 0.25, f)); };
Effect