OpenGL 点精灵

简述

点精灵使使用片元着色器渲染的OpenGL点。运行的时候需要考虑点内的片元坐标,这个坐标由内置的两维向量gl_PointCoord保存,这个变量最常见的作用是当作纹理坐标使用或者用于计算颜色和覆盖率。

给点精灵贴上纹理

在OpenGL绘制的点,只有一个位置坐标。如果要给一个点贴上纹理,则需要使用内置变量gl_PointCoord来查询纹理中的纹素,这样就可以生成一个带纹理的点精灵了。环境:OPenGL3.3,glfw,glew。

简单的点精灵示例源码

着色器

//顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;void main()
{gl_Position = vec4(aPos.x, aPos.y, 0, 1.0);//设置点精灵的大小gl_PointSize = 80;
};
//片元着色器
#version 
330 core
uniform sampler2D sprite_texture;
out vec4 FragColor;void main()
{FragColor = texture(sprite_texture, gl_PointCoord);
};


应用程序

在应用程序中需要生成一个纹理对象,并关联到着色器,这跟普通的纹理映射相同。绘制前开启点精灵相关功能,绘制类型必须为GL_POINTS。

注意:点精灵的大小有两种方法绘制,一种是在应用程序中调用函数glPointSize()设置点的大小,这种方式点的大小是固定的,不会随着相机的离远而变小。还有一种是在顶点着色器中设置,使用内置的输出变两gl_PointSize来设置,可以根据它们相对于近平面的距离来进行一个线性映射。

#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;
int main()
{//---------------------------------------------------------------------------------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[] = {// 位置              // 颜色0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,   // 右下-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,   // 左下0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f    // 顶部};// ---------------纹理1 ---------------unsigned int texture1;// 创建纹理glGenTextures(1, &texture1);//绑定纹理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);//图像的宽度、高度和颜色通道的个数int width, height, nrChannels;stbi_set_flip_vertically_on_load(true);//加载纹理unsigned char *data = stbi_load("bricks2.jpg", &width, &height, &nrChannels, 0);//如果加载成功if (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;}//释放图像内存stbi_image_free(data);//创建缓存对象unsigned int VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);//绑定VAOglBindVertexArray(VAO);//绑定VBOglBindBuffer(GL_ARRAY_BUFFER, VBO);//把顶点数组复制到VBOglBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 位置属性,第一个参数是着色器位置glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// 颜色属性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);//点精灵设置glEnable(GL_POINT_SPRITE);glEnable(GL_PROGRAM_POINT_SIZE);glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE);//激活着色器ourShader.use();glBindTexture(GL_TEXTURE_2D, texture1);//绑定顶点缓存对象glBindVertexArray(VAO);//开始绘制glDrawArrays(GL_POINTS, 0, 3);//解除绑定glBindVertexArray(0);//交换缓存glfwSwapBuffers(window);//查询io事件glfwPollEvents();}//删除顶点数组对象和顶点缓冲对象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);}
}


 效果

解析点精灵的颜色和形状

纹理的分辨率是有限的,我们并不仅限于从纹理加载数据。gl_PointCoord非常精确,把gl_PointCoord当作点的圆心,然后计算片元离点精灵中心的距离,如果大于0.25,就使用discard关键字拒绝这个片元,这样就生成了一个半径为0.25的圆了。

示例源码

注意:只需要修改着色器代码即可,应用程序同上

//顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;void main()
{gl_PointSize = 80;gl_Position = vec4(aPos.x, aPos.y, 0, 1.0);
};
//片元着色器
#version 330 coreuniform 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));
};

效果


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部