2013-08-12 43 views
5

Tôi đang gặp phải sự cố lạ trong ứng dụng đơn giản (qua GLFW3) đơn giản của tôi. Mặc dù VSync được kích hoạt (tốc độ khung hình gần như ổn định 60 khung hình/giây), chuyển động của hình tam giác quay không phải lúc nào cũng trơn tru - gần giống như một số khung hình bị bỏ qua đôi khi. Tôi đã thử nhìn vào sự khác biệt thời gian giữa các cuộc gọi liên tiếp đến glSwapBuffers(), nhưng những người có vẻ khá nhất quán.hiển thị hình ảnh lạ lộng lẫy

Tôi có làm gì sai không? Tôi có nên sử dụng một số loại lọc chuyển động mờ để làm cho nó mượt mà hơn không?

Mã:

#include <cstdlib> 
#include <cstdio> 
#include <cmath> 
#include <cfloat> 
#include <cassert> 
#include <minmax.h> 
#include <string> 
#include <iostream> 
#include <fstream> 
#include <vector> 

#include <Windows.h> 
#include <GL/glew.h> 
#include <gl/GLU.h> 
//#include <GL/GL.h> 
#include <GLFW/glfw3.h> 
#include <glm/glm.hpp> 
#include <glm/gtc/type_ptr.hpp> 

#ifdef _WIN32 
#pragma warning(disable:4996) 
#endif 

static int swap_interval; 
static double frame_rate; 


GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){ 

    // Create the shaders 
    GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); 
    GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); 

    // Read the Vertex Shader code from the file 
    std::string VertexShaderCode; 
    std::ifstream VertexShaderStream(vertex_file_path, std::ios::in); 
    if(VertexShaderStream.is_open()){ 
     std::string Line = ""; 
     while(getline(VertexShaderStream, Line)) 
      VertexShaderCode += "\n" + Line; 
     VertexShaderStream.close(); 
    }else{ 
     printf("Impossible to open %s. Are you in the right directory ? Don't forget to read the FAQ !\n", vertex_file_path); 
     return 0; 
    } 

    // Read the Fragment Shader code from the file 
    std::string FragmentShaderCode; 
    std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in); 
    if(FragmentShaderStream.is_open()){ 
     std::string Line = ""; 
     while(getline(FragmentShaderStream, Line)) 
      FragmentShaderCode += "\n" + Line; 
     FragmentShaderStream.close(); 
    } 

    GLint Result = GL_FALSE; 
    int InfoLogLength; 

    // Compile Vertex Shader 
    printf("Compiling shader : %s\n", vertex_file_path); 
    char const * VertexSourcePointer = VertexShaderCode.c_str(); 
    glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL); 
    glCompileShader(VertexShaderID); 

    // Check Vertex Shader 
    glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); 
    if (Result != GL_TRUE) 
    { 
     glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 
     if (InfoLogLength > 0){ 
      std::vector<char> VertexShaderErrorMessage(InfoLogLength+1); 
      glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); 
      printf("%s\n", &VertexShaderErrorMessage[0]); 
     } 
    } 


    // Compile Fragment Shader 
    printf("Compiling shader : %s\n", fragment_file_path); 
    char const * FragmentSourcePointer = FragmentShaderCode.c_str(); 
    glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL); 
    glCompileShader(FragmentShaderID); 

    // Check Fragment Shader 
    glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); 
    if (Result != GL_TRUE) 
    { 
     glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 
     if (InfoLogLength > 0){ 
      std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1); 
      glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); 
      printf("%s\n", &FragmentShaderErrorMessage[0]); 
     } 
    } 

    // Link the program 
    printf("Linking program\n"); 
    GLuint ProgramID = glCreateProgram(); 
    glAttachShader(ProgramID, VertexShaderID); 
    glAttachShader(ProgramID, FragmentShaderID); 
    glLinkProgram(ProgramID); 

    // Check the program 
    glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); 
    if (Result != GL_TRUE) 
    { 
     glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); 
     if (InfoLogLength > 0){ 
      std::vector<char> ProgramErrorMessage(InfoLogLength+1); 
      glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); 
      printf("%s\n", &ProgramErrorMessage[0]); 
     } 
    } 
#ifdef _DEBUG 
    glValidateProgram(ProgramID); 
#endif 

    glDeleteShader(VertexShaderID); 
    glDeleteShader(FragmentShaderID); 

    return ProgramID; 
} 


static void framebuffer_size_callback(GLFWwindow* window, int width, int height) 
{ 
    glViewport(0, 0, width, height); 
} 

static void set_swap_interval(GLFWwindow* window, int interval) 
{ 
    swap_interval = interval; 
    glfwSwapInterval(swap_interval); 
} 

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) 
{ 
    if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 
     set_swap_interval(window, 1 - swap_interval); 
} 

static bool init(GLFWwindow** win) 
{ 
    if (!glfwInit()) 
     exit(EXIT_FAILURE); 

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); 

    // creating a window using the monitor param will open it full screen 
    const bool useFullScreen = false; 
    GLFWmonitor* monitor = useFullScreen ? glfwGetPrimaryMonitor() : NULL; 
    *win = glfwCreateWindow(640, 480, "", monitor, NULL); 
    if (!(*win)) 
    { 
     glfwTerminate(); 
     exit(EXIT_FAILURE); 
    } 
    glfwMakeContextCurrent(*win); 

    GLenum glewError = glewInit(); 
    if(glewError != GLEW_OK) 
    { 
     printf("Error initializing GLEW! %s\n", glewGetErrorString(glewError)); 
     return false; 
    } 
    //Make sure OpenGL 2.1 is supported 
    if(!GLEW_VERSION_2_1) 
    { 
     printf("OpenGL 2.1 not supported!\n"); 
     return false; 
    } 

    glfwMakeContextCurrent(*win); 
    glfwSetFramebufferSizeCallback(*win, framebuffer_size_callback); 
    glfwSetKeyCallback(*win, key_callback); 

    // get version info 
    const GLubyte* renderer = glGetString (GL_RENDERER); // get renderer string 
    const GLubyte* version = glGetString (GL_VERSION); // version as a string 
    printf("Renderer: %s\n", renderer); 
    printf("OpenGL version supported %s\n", version); 

    return true; 
} 
std::string string_format(const std::string fmt, ...) { 
    int size = 100; 
    std::string str; 
    va_list ap; 
    while (1) { 
     str.resize(size); 
     va_start(ap, fmt); 
     int n = vsnprintf((char *)str.c_str(), size, fmt.c_str(), ap); 
     va_end(ap); 
     if (n > -1 && n < size) { 
      str.resize(n); 
      return str; 
     } 
     if (n > -1) 
      size = n + 1; 
     else 
      size *= 2; 
    } 
    return str; 
} 
int main(int argc, char* argv[]) 
{ 
    srand(9); // constant seed, for deterministic results 

    unsigned long frame_count = 0; 

    GLFWwindow* window; 
    init(&window); 

    // An array of 3 vectors which represents 3 vertices 
    static const GLfloat g_vertex_buffer_data[] = { 
     -1.0f, -1.0f, 0.0f, 
     1.0f, -1.0f, 0.0f, 
     0.0f, 1.0f, 0.0f, 
    }; 

    GLuint vbo; 
    glGenBuffers(1, &vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 

    // acclocate GPU memory and copy data 
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW); 

    unsigned int vao = 0; 
    glGenVertexArrays (1, &vao); 
    glBindVertexArray (vao); 
    glEnableVertexAttribArray (0); 
    glBindBuffer (GL_ARRAY_BUFFER, vbo); 
    glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, 0); 

    // Create and compile our GLSL program from the shaders 
    GLuint programID = LoadShaders("1.vert", "1.frag"); 

    // Use our shader 
    glUseProgram(programID); 

    GLint locPosition = glGetAttribLocation(programID, "vertex"); 
    assert(locPosition != -1); 

    glm::mat4 world(1.0f); 
    GLint locWorld = glGetUniformLocation(programID, "gWorld"); 
    assert(locWorld != -1 && "Error getting address (was it optimized out?)!"); 
    glUniformMatrix4fv(locWorld, 1, GL_FALSE, glm::value_ptr(world)); 
    GLenum err = glGetError(); 

    GLint loc = glGetUniformLocation(programID, "time"); 
    assert(loc != -1 && "Error getting uniform address (was it optimized out?)!"); 

    bool isRunning = true; 
    while (isRunning) 
    { 
     static float time = 0.0f; 
     static float oldTime = 0.0f; 
     static float fpsLastUpdateTime = 0.0f; 
     oldTime = time; 
     time = (float)glfwGetTime(); 
     static std::string fps; 

     glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
     glUseProgram (programID); 
     glUniform1f(loc, time); 
     glBindVertexArray (vao); 
     glDrawArrays (GL_TRIANGLES, 0, 3); 
     glfwSwapBuffers(window); 
     glfwPollEvents(); 
     isRunning = !glfwWindowShouldClose(window); 

     float dT = time-oldTime; 
     if (time-fpsLastUpdateTime > 0.5) 
     { 
      static const char* fmt = "frame rate: %.1f frames per second";  
      glfwSetWindowTitle(window, string_format(fmt, 1.0f/(dT)).c_str()); 
      fpsLastUpdateTime = time; 
     } 
    } 

    glfwDestroyWindow(window); 
    glfwTerminate(); 

    return 0; 
} 


//////////////////////////////////////// 
// 1.frag 
//////////////////////////////////////// 
#version 330 core 

// Ouput data 
out vec3 color; 

void main() 
{ 
    // Output color = red 
    color = vec3(1,0,0); 
} 

////////////////////////////////////////////// 
// 1.vert 
////////////////////////////////////////////// 
#version 330 core 

// Input vertex data, different for all executions of this shader. 
in vec3 vertex; 

uniform mat4 gWorld; 
uniform float time; 

void main() 
{ 
    gl_Position = gWorld * vec4(vertex, 1.0f); 
    gl_Position.x += sin(time); 
    gl_Position.y += cos(time)/2.0f; 
    gl_Position.w = 1.0; 
} 

OK. Tôi về nhà và làm nhiều thử nghiệm hơn.

Trước tiên, tôi đã cố vô hiệu hóa V-Sync, nhưng tôi không thể! Tôi đã phải vô hiệu hóa các hiệu ứng máy tính để bàn của Windows (Aero) để có thể làm như vậy, và lo và nhìn - một khi Aero đã bị vô hiệu hóa, nói lắp biến mất (với V-Sync trên).

Sau đó, tôi đã thử nghiệm nó với V-Sync tắt, và tất nhiên, tôi có tỷ lệ khung hình cao hơn nhiều với các giọt nước mắt dự kiến ​​không thường xuyên.

Sau đó, tôi đã thử nghiệm ở chế độ toàn màn hình. Kết xuất trơn tru với Aero và không có nó.

Tôi không thể tìm thấy bất kỳ ai khác chia sẻ sự cố này. Bạn có nghĩ rằng đó là một lỗi GLFW3? một vấn đề trình điều khiển/phần cứng (Tôi có GTS450 với các trình điều khiển mới nhất)?

Cảm ơn tất cả các bạn đã trả lời. Tôi đã học được rất nhiều, nhưng vấn đề của tôi vẫn chưa được giải quyết.

+0

Bạn có thể làm cho tốc độ hình tam giác của bạn không phụ thuộc vào tốc độ khung hình, ngay cả khi khung được bỏ qua, bạn sẽ không thấy vấn đề. –

+0

@Luke, bố trí tam giác phụ thuộc vào thời gian của hệ thống - vị trí này là chính xác về mặt vật lý phần lớn thời gian. – liorda

+0

@liorda Tôi không chắc chỉ sử dụng thời gian là đủ cho điều đó, shoudn't bạn sử dụng DT thay vì thời gian trong bóng đổ? –

Trả lời

3

Nếu không thấy vấn đề nói lắp này khó có thể nói vấn đề là gì. Nhưng ấn tượng đầu tiên của chương trình của bạn là ok.
Vì vậy, tôi đoán bạn quan sát thấy một khung hình một lần trong một thời gian được hiển thị hai lần. Dẫn đến một nói lắp rất nhỏ. Điều này xảy ra thường khi bạn cố gắng xuất 60 khung hình trên màn hình 60Hz với vsync.
Trong một thiết lập như vậy, bạn không được bỏ lỡ một khoảng thời gian vsync hoặc bạn sẽ thấy một nói lắp, vì khung hình hiển thị hai lần.
Mặt khác, gần như không thể đảm bảo điều này vì bộ lập lịch trên nền tảng cửa sổ lập lịch trình cho 15ms (về điều đó tôi không biết giá trị chính xác bằng trái tim).
Vì vậy, có thể một chủ đề ưu tiên cao hơn sẽ sử dụng CPU và chuỗi trình bày của bạn không thể hoán đổi bộ đệm cho khung mới đúng lúc. Khi bạn tăng giá trị, ví dụ: 120 khung hình trên màn hình 120 Hz, bạn sẽ thấy những cửa sổ đó thậm chí thường xuyên hơn.
Vì vậy, tôi không biết giải pháp nào về cách bạn có thể ngăn chặn điều này trên nền tảng Windows. Nhưng nếu ai đó biết tôi cũng rất vui khi biết điều đó.

+0

Từ kinh nghiệm của tôi Windows NT có từ 10-15 ms quanta cho các cửa sổ nền trước. Hành vi lập lịch trình mặc định là ưu tiên cửa sổ nền trước và cung cấp thời gian lớn hơn, và dĩ nhiên nó là ưu tiên trước. Nếu bạn đặt ưu tiên luồng thành thời gian thực, nó sẽ sửa đổi thêm các quy tắc, nhưng gần như không bao giờ thực sự cần thiết. –

+0

Vì vậy, về cơ bản không có gì sai với mã của tôi, nó chỉ là cách mọi thứ hoạt động trong các hệ thống preemptive? – liorda

2

Thật khó để nói mà không hình dung vấn đề của bạn nhưng trừ khi chúng tôi đang nói về một số nói lắp nghiêm trọng nó hiếm khi là một vấn đề kết xuất. Chuyển động/vật lý trong chương trình của bạn được xử lý/xử lý bởi CPU. Cách bạn đang triển khai hoạt ảnh của mình, được xử lý theo cách chỉ phụ thuộc vào CPU.

Điều này có nghĩa là:

Giả sử bạn đang xoay tam giác của mình theo số tiền cố định trong mỗi chu kỳ CPU. Điều này rất phụ thuộc vào thời gian một chu kỳ CPU cần hoàn thành. Những thứ như khối lượng công việc cpu có thể có tác động rất lớn đến kết quả màn hình của bạn (không nhất thiết là mặc dù). Và nó thậm chí còn không chiếm nhiều CPU để nhận thấy sự khác biệt. Tất cả phải mất là một quá trình nền để thức dậy và truy vấn cập nhật.Điều này có thể dẫn đến một 'tăng đột biến' trong đó có thể được quan sát như là một tạm dừng nhỏ trong dòng hoạt hình của bạn (do sự chậm trễ nhỏ CPU có thể gây ra trong chu kỳ hoạt hình của bạn). Điều này có thể được hiểu là một nói lắp.

Bây giờ hiểu được ở trên có một số cách để giải quyết vấn đề của bạn (nhưng theo ý kiến ​​của tôi không đáng để đầu tư cho những gì bạn đang cố gắng làm ở trên). Bạn cần phải tìm một cách để có các bước hoạt hình phù hợp (với biên độ nhỏ cho biến thể).

Đây là một bài viết tuyệt vời để khám phá: http://gafferongames.com/game-physics/fix-your-timestep/

Cuối cùng hầu hết các phương pháp thực hiện trên sẽ gây ra một dòng chảy vẽ tốt hơn. Nhưng vẫn không phải tất cả chúng đều đảm bảo độ chính xác dựng hình vật lý. Nếu không thử nó ra bản thân mình, tôi sẽ nói rằng một trong những sẽ phải đi xa như thực hiện nội suy trong quá trình dựng hình của mình để đảm bảo vẽ mịn nhất có thể.

Bây giờ những gì tôi muốn giải thích cho bạn nhất, là nói lắp thường được gây ra bởi CPU bởi vì nó can thiệp trực tiếp với cách xử lý vật lý của bạn. Nhưng nhìn chung, việc sử dụng thời gian để xử lý vật lý của bạn và nội suy bên trong chu kỳ dựng hình của bạn là một chủ đề chắc chắn đáng để khám phá.

+0

Có thực tế phổ biến để sử dụng chuyển động mờ theo cách "nội suy trực quan"? – liorda

+0

Tôi không chắc bạn đang nói về điều gì. Đây là một chủ đề khá lớn để xây dựng. Có một cái nhìn tại câu hỏi này http://gamedev.stackexchange.com/questions/12754/how-to-interpolate-between-two-game-states Nó cung cấp cho một sự hiểu biết tốt hơn về ở trên. – Bisder

4

Đó là chế độ thành phần Windows dwm (Desktop Window Manager) lạ và glfwSwapBuffers() vấn đề tương tác. Tôi đã không xuống đến gốc của vấn đề được nêu ra. Nhưng bạn có thể workaround nói lắp bằng cách thực hiện một trong các cách sau:

  • đi toàn màn hình vô hiệu hóa phần
  • cửa sổ DWM (xem câu trả lời của tôi để Linear movement stutter)
  • phép đa lấy mẫu: glfwWindowHint(GLFW_SAMPLES, 4);
Các vấn đề liên quan