2017-01-09 87 views
0

我正在尝试使用点精灵来使用OpenGL 3做粒子系统。在OpenGL中使用点精灵进行深度测试

我使用了一个带GL_STREAM_DRAW的VBO,我在其中放置了每个粒子的坐标。 在每个帧中,我用新的粒子坐标更新VBO。使用GL_VERTEX_PROGRAM_POINT_SIZE简单地使用GL_POINTS呈现粒子。

我注意到有些颗粒被其他颗粒覆盖,尽管事实上它们应该更接近相机。

点精灵实际上是由绘制调用的顺序而不是深度绘制,这创造这样的情况:

The farthest particle is drawn first, the closes particle is drawn second.

这里最远的颗粒在第一次绘制时,关闭粒子被吸入第二。正如所料,壁橱里的微粒完全覆盖了它后面的一个。

This time the order is reversed.

这里,绘制顺序被反转,导致最远颗粒是可见的。

我请尝试使用

glEnable(GL_DEPTH_TEST); 
    glDepthMask(GL_TRUE); 
    glDepthFunc(GL_LEQUAL); 
    glDepthRange(0.f, 1.f); 
glEnable(GL_DEPTH_CLAMP); 

OpenGL的深度测试,但它只是导致没有被绘制。

据我所知,解决这个问题的一种方法是按照深度对粒子进行重新排序,但这种解决方案对于许多粒子来说CPU会非常昂贵,所以有办法对点精灵进行适当的深度测试GPU?

顶点着色器使用用于绘制颗粒如下:

#version 330 

layout(location = 0) in vec4 position; 

uniform float time; 
uniform mat4 camera; 

smooth out float dist; 

void main() 
{ 
    vec4 cameraPos = position + vec4(0.0, 0.0, -1.0, 0.0); 
    gl_Position = camera * cameraPos; 
    dist = sqrt(dot(camera * cameraPos, position)); 
    gl_PointSize = 15.0/dist; 
} 

片段着色器:

#version 330 

out vec4 colour; 
uniform float time; 

smooth in float dist; 

float map(float value, float inMin, float inMax, float outMin, float outMax) { 
    return outMin + (outMax - outMin) * (value - inMin)/(inMax - inMin); 
} 

void main() 
{ 
// colour = vec4(pos.x, pos.y, 1.0, 1.0); 
    if(dot(gl_PointCoord-0.5,gl_PointCoord-0.5)>0.25) 
     discard; 
    else { 
     float g = (dot(gl_PointCoord-0.5,gl_PointCoord-0.5) > 0.22 ? 0.6 : map(dot(gl_PointCoord-0.5,gl_PointCoord-0.5), 0.0, 0.21, 0.0, 0.6)); 
     colour = vec4(g, g*sin(time)*sin(time)*cos(time), sin(dist), 1.0); 
    } 
} 

的完整代码(减去一些样板代码):

#include <glad/glad.h> 
#include <GLFW/glfw3.h> 
#include <glm/glm.hpp> 
#include <glm/gtc/type_ptr.hpp> 
#include <glm/vec4.hpp> 
#include <glm/mat4x4.hpp> 
#include <glm/gtc/matrix_transform.hpp> 
#include <glm/trigonometric.hpp> 
#include <algorithm> 
#include <string> 
#include <iostream> 
#include <vector> 
#include <random> 
#include <cmath> 

#include "tools.h" 
#include "shader.h" 
#include "data.h" 

#define BENCHMARK 230000 
#define MAX_POINT 2 
#define TTL 100 

void init_program(GLuint* program) 
{ 
    std::vector<GLuint> shaders; 
    shaders.push_back(create_shader(GL_VERTEX_SHADER, read_file("data/particle.vs"))); 
    shaders.push_back(create_shader(GL_FRAGMENT_SHADER, read_file("data/particle.fs"))); 
    *program = create_program(shaders); 
    std::for_each(shaders.begin(), shaders.end(), glDeleteShader); 
} 

bool first=true; 
void create_new_point(Point* p) 
{ 
    // Testing draw order 
    if(first) 
     p->pos = glm::vec4(0.f, 0.f, 0.f, 1.f); 
    else 
     p->pos = glm::vec4(0.f, 0.f, 0.8, 1.f); 

    p->dir = glm::vec4(0.f, 0.f, 0.f, 0.f); 
    p->ttl = TTL+(TTL*(distrib(gen)/2.0)); 
    first = false; 
} 

void update_point(Point* p, double dt) 
{ 
    if((p->ttl - dt) <= 0) 
     create_new_point(p); 
    else 
    { 
     glm::vec4 speed(dt/2.0); 
     p->pos += (p->dir*speed); 
     p->ttl = p->ttl - dt; 
    } 
} 

void vbo_point(std::vector<Point>& points, float* data, GLuint* vbo, bool update) 
{ 
    for(size_t n=0; n<points.size(); ++n) 
    { 
     if(update) 
     { 
      data[n*4] = points[n].pos.x; 
      data[n*4+1] = points[n].pos.y; 
      data[n*4+2] = points[n].pos.z; 
      data[n*4+3] = points[n].pos.w; 
     } 
     else 
     { 
      data[n*4] = 0; 
      data[n*4+1] = 0; 
      data[n*4+2] = 0; 
      data[n*4+3] = 0; 
     } 
    } 

    glBindBuffer(GL_ARRAY_BUFFER, *vbo); 
    if(update) 
     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*4*points.size(), data); 
    else 
     glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*points.size(), data, GL_STREAM_DRAW); 
    glBindBuffer(GL_ARRAY_BUFFER, 0); 
} 

int main(void) 
{ 
    GLFWwindow* window; 
    if (!glfwInit()) 
     return -1; 

    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); 

    window = glfwCreateWindow(1280, 768, "Hello World", NULL, NULL); 
    if (!window) 
    { 
     glfwTerminate(); 
     return -1; 
    } 

    glfwMakeContextCurrent(window); 
    gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); 

    // Init data 
    GLuint vbo, vao, program; 
    glGenBuffers(1, &vbo); 
    init_program(&program); 

    // VAO 
    glGenVertexArrays(1, &vao); 
    glBindVertexArray(vao); 

    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); 

    /* 
    glEnable(GL_DEPTH_TEST); 
     glDepthMask(GL_TRUE); 
     glDepthFunc(GL_LEQUAL); 
     glDepthRange(0.f, 1.f); 
    glEnable(GL_DEPTH_CLAMP); 

    glEnable(GL_BLEND) ; 
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
    */ 

    // Time data 
    double prev = 0.0; 
    double curr = 0.0; 
    double frameTime = 0.0; 

    // Init Points 
    std::vector<Point> points; 
    for(size_t n=0; n<MAX_POINT; ++n) 
    { 
     Point tmp = {glm::vec4(0), glm::vec4(0), 0}; 
     points.push_back(tmp); 
    } 
    float* data = new float[4*points.size()]; 
    for(size_t n=0; n<points.size(); ++n) 
     update_point(&points[n], 0); 
    vbo_point(points, data, &vbo, false); 

    glfwSwapInterval(1); 

    GLint time = glGetUniformLocation(program, "time"); 
    GLint camera_location = glGetUniformLocation(program, "camera"); 
    glm::mat4 camera_matrix = glm::perspective(glm::radians(45.f), 1.33f, 0.1f, 10.f); 

    /* Loop until the user closes the window */ 
    while (!glfwWindowShouldClose(window)) 
    { 
     curr = glfwGetTime(); 
     /* Render here */ 
     glClear(GL_COLOR_BUFFER_BIT); 
     glClearColor(1.f, 1.f, 1.f, 0.f); 

     glUseProgram(program); 
     glUniform1f(time, glfwGetTime()); 
     glUniformMatrix4fv(camera_location, 1, GL_FALSE, glm::value_ptr(camera_matrix)); 

     glBindBuffer(GL_ARRAY_BUFFER, vbo); 
     glEnableVertexAttribArray(0); 
     glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); 
     glDrawArrays(GL_POINTS, 0, points.size()); 
     glDisableVertexAttribArray(0); 
     glUseProgram(0); 

     /* Swap front and back buffers */ 
     glfwSwapBuffers(window); 

     /* Poll for and process events */ 
     glfwPollEvents(); 

     for(size_t n=0; n<points.size(); ++n) 
      update_point(&points[n], frameTime); 
     vbo_point(points, data, &vbo, true); 

     std::cout << std::fixed; 
     std::cout.precision(8); 
     std::cout << "\rfps: " << 1.f/frameTime << " | Point drawed :" << points.size() 
      << " | TTL1: " << points[0].ttl; 

     prev = glfwGetTime(); 
     frameTime = prev-curr; 
    } 

    delete[] data; 

    glfwTerminate(); 
    return 0; 
} 
+2

请显示验证码 –

+0

对不起,在发布完整的问题之前,我按了tab +错误地输入。你现在可以看到更新。 – Maeln

+0

你最近怎么画?你的矩阵是如何设置的?任何着色器?或者你在使用固定功能流水线吗? –

回答

0

问题实际上与2问题有关:

1 - 深度缓冲区从未被清除,因为用户derhass在评论中提到。

2 - 顶点缓冲区中的点大小以错误的方式计算。透视矩阵仅应用于相机位置而不应用于顶点位置。它应该是dist = distance(cameraPos, position));而不是dist = sqrt(dot(camera * cameraPos, position));