2017-01-09 2 views
0

Я пытаюсь сделать систему частиц с помощью OpenGL 3 с помощью точечного спрайта.Тест глубины с точным спрайтом в OpenGL

Я использую VBO с GL_STREAM_DRAW, где я помещаю координату каждой частицы. Во время каждого кадра я обновляю VBO с новой координатой частиц. Частица просто отображается GL_POINTS, используя GL_VERTEX_PROGRAM_POINT_SIZE.

Я заметил, что некоторые частицы, наложенные другим, несмотря на то, что они должны были быть ближе к камере.

Точка спрайт фактически обращается порядка вызова отрисовки и не глубиной, которые создают ситуацию так:

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

Здесь дальняя частица рисуется первым, частица закрывается рисуется второй. Как и ожидалось, частицы шкафа полностью покрывают ту, которая находится за ней.

This time the order is reversed.

Здесь порядок дро восстанавливается в результате самой дальней частицы будут видимы.

Я пытаюсь с помощью тестирования глубины OpenGL с использованием

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

Но это только в результате ничего не втягиваются.

Как я понимаю, одним из способов решения этой проблемы является переупорядочивание частиц по глубине, но это решение было бы очень дорогостоящим для процессора для многих частиц, поэтому есть способ провести надлежащее тестирование глубины для точечного спрайта на 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));

Смежные вопросы