2015-06-16 7 views
5

Pracuję nad symulacją chmur (rzeczywistych chmur), gdzie chmury są symulowane za pomocą punktów 3D, a następnie rzutowane na dwuwymiarową mapę cieplną o wielkości 640x480. Liczba punktów wynosi około 50k, co jest tak małe, jak tylko mogę przejść bez rozbijania symulacji, ale nie mogę znaleźć sposobu na wykonanie tego z dowolną prędkością (zwykle trwa to 3-5 sekund czasu wykonywania).Czy można zbudować mapę cieplną z danych punktowych 60 razy na sekundę?

Przypuszczam, że moje pytanie brzmi, czy jest to możliwe, aby przeciętny komputer mógł to jeszcze zrobić? Zwykle nie doceniam szybkości komputerów w dzisiejszych czasach, ale mogę przecenić je w tym przypadku. Nie zoptymalizowałem jeszcze symulacji, ale jeśli nie jest to możliwe, dobrze byłoby wiedzieć i uratować teraz kłopot.

Jeśli jest to możliwe, czy istnieje technika, która może okazać się przydatna do przekształcenia danych punktu w mapę cieplną wystarczająco szybko, aby aktualizować 60 razy na sekundę? To naprawdę tylko patrzenie na dane punktu i zapisywanie do tablicy 2D wyników po transformacji, więc myślę, że jest to głównie związane z wyszukiwaniem pamięci.

+0

myślę, że trzeba zrobić jakieś wstępne oszacowanie liczby obliczeń RE wymagane do wykonania projekcji, na piksel mapy cieplnej i na punkt 3D. Nie sądzę, żeby istniał jakiś standard, żeby to było oczywiste. Nie moja okolica, więc mógłbym się mylić. – unwind

+0

Związany z [this] (http://stackoverflow.com/q/30857894/1888983)? Rozpryskiwanie cząstek o wielkości 50k do obrazu 640x480 będzie niewiarygodnie szybkie (chyba że wypełnią one cały obraz). Niedawno napisałem [to] (http://stackoverflow.com/a/30837947/1888983). – jozxyqk

+0

Powinieneś przeprowadzić analizę porównawczą, aby sprawdzić, czy projekcje 3D do 2D są naprawdę powolne przed zoptymalizowaniem aktualizacji map cieplnych (tj. Jaki odsetek 3-5 sekund renderowania, jaki obecnie zajmuje się projekcjami?) –

Odpowiedz

6

Jest to zdecydowanie wykonalne, prawdopodobnie nawet jeśli obliczenia są wykonywane przez procesor. Idealnie powinieneś używać GPU. Wymagane interfejsy API to: OpenCL lub od kiedy renderujesz wyniki, możesz chcieć skorzystać z Compute Shaders.

Obie techniki umożliwiają napisanie małego programu (modułu cieniującego), który działa na pojedynczym elemencie (punkcie). Wszystkie one są uruchamiane równolegle na GPU, co powinno pozwolić im działać bardzo szybko.

3

Tak, jeśli dane są już wstępnie obliczane w pamięci

Wystarczy go wypróbować z SDL faktur (lub tekstur OpenGL bezpośrednio, co jest, co SDL zastosowania):

#include <math.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 

#include <SDL2/SDL.h> 

#define COLOR_MAX 255 

double common_get_secs(void) { 
    struct timespec ts; 
    timespec_get(&ts, TIME_UTC); 
    return ts.tv_sec + (1e-9 * ts.tv_nsec); 
} 
const double COMMON_FPS_GRANULARITY_S = 0.5; 
double common_fps_last_time_s; 
unsigned int common_fps_nframes; 
void common_fps_init() { 
    common_fps_nframes = 0; 
    common_fps_last_time_s = common_get_secs(); 
} 
void common_fps_update_and_print() { 
    double dt, current_time_s; 
    current_time_s = common_get_secs(); 
    common_fps_nframes++; 
    dt = current_time_s - common_fps_last_time_s; 
    if (dt > COMMON_FPS_GRANULARITY_S) { 
     printf("FPS = %f\n", common_fps_nframes/dt); 
     common_fps_last_time_s = current_time_s; 
     common_fps_nframes = 0; 
    } 
} 

int main(void) { 
    SDL_Event event; 
    SDL_Renderer *renderer = NULL; 
    SDL_Texture *texture = NULL; 
    SDL_Window *window = NULL; 
    Uint8 *base; 
    int pitch; 
    void *pixels = NULL; 
    const unsigned int 
     WINDOW_WIDTH = 500, 
     WINDOW_HEIGHT = WINDOW_WIDTH; 
    const double 
     SPEED = WINDOW_WIDTH/10.0, 
     CENTER_X = WINDOW_WIDTH/2.0, 
     CENTER_Y = WINDOW_HEIGHT/2.0, 
     PERIOD = WINDOW_WIDTH/10.0, 
     PI2 = 2.0 * acos(-1.0); 
    double dt, initial_time; 
    float z; 
    unsigned int x, xc, y, yc; 

    SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO); 
    SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_WIDTH, 0, &window, &renderer); 
    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, 
     SDL_TEXTUREACCESS_STREAMING, WINDOW_WIDTH, WINDOW_HEIGHT); 
    initial_time = common_get_secs(); 
    common_fps_init(); 
    while (1) { 
     dt = common_get_secs() - initial_time; 
     SDL_LockTexture(texture, NULL, &pixels, &pitch); 
     for (x = 0; x < WINDOW_WIDTH; x++) { 
      for (y = 0; y < WINDOW_HEIGHT; y++) { 
       xc = CENTER_X - x; 
       yc = CENTER_Y - y; 
       /*z = COLOR_MAX * 0.5 * (1.0 + (sin(PI2 * (sqrt(xc*xc + yc*yc) - SPEED * dt)/PERIOD)));*/ 
       z = (int)(x + y + SPEED * dt) % COLOR_MAX; 
       base = ((Uint8 *)pixels) + (4 * (x * WINDOW_WIDTH + y)); 
       base[0] = 0; 
       base[1] = 0; 
       base[2] = z; 
       base[3] = COLOR_MAX; 
      } 
     } 
     SDL_UnlockTexture(texture); 
     SDL_RenderCopy(renderer, texture, NULL, NULL); 
     SDL_RenderPresent(renderer); 
     common_fps_update_and_print(); 
     if (SDL_PollEvent(&event) && event.type == SDL_QUIT) 
      break; 
    } 
    SDL_DestroyRenderer(renderer); 
    SDL_DestroyWindow(window); 
    SDL_Quit(); 
    return EXIT_SUCCESS; 
} 

skompilować :

gcc -Wall -std=c11 heatmap.c -lSDL2 -lm 

na Ubuntu 16.04, prostszej obliczenia:

z = (x + y + SPEED * dt) % COLOR_MAX 

osiąga 300 FPS na laptopie Thinkpad T430 z Nvidia NVS 5400M (2012 mid-end).

Więc oczywiście wstępnie obliczony wynik na pamięć będzie jeszcze szybciej.

Jeżeli obliczenie jest trochę bardziej skomplikowana, jednakże:

z = COLOR_MAX * 0.5 * (1.0 + (sin(PI2 * (sqrt(xc*xc + yc*yc) - SPEED * dt)/PERIOD))) 

FPS jest tylko 30, więc szybko staje się ograniczenie kalkulacja.

Jeśli nie można uruchomić obliczenia na tyle szybko, będzie prawdopodobnie trzeba przechowywać na dysku, aby nie przepełnić pamięć, a potem to wszystko o benchmarking dysku + metod kompresji (kodeków).

Fragment shadery

Jeśli można uruchomić obliczenia na cieniującego fragmentu, można zrobić dużo bardziej skomplikowane rzeczy, jednak w czasie rzeczywistym.

Za pomocą poniższego kodu, bardziej skomplikowane obliczenia są przeprowadzane pod numerem 3k FPS!

Ale będzie trudniej wprowadzić, więc upewnij się, że go potrzebujesz.

#include <math.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 

#include <SDL2/SDL.h> 
#define GLEW_STATIC 
#include <GL/glew.h> 

static const GLuint WIDTH = 500; 
static const GLuint HEIGHT = 500; 
static const GLchar* vertex_shader_source = 
    "#version 120\n" 
    "attribute vec2 coord2d;\n" 
    "void main(void) {\n" 
    " gl_Position = vec4(coord2d, 0.0, 1.0);\n" 
    "}\n"; 
static const GLchar* fragment_shader_source = 
    "#version 120\n" 
    "uniform float pi2;\n" 
    "uniform float time;\n" 
    "uniform float width;\n" 
    "uniform float height;\n" 
    "uniform float periods_x;\n" 
    "uniform float periods_y;\n" 
    "void main(void) {\n" 
    " float center_x = width/2.0;" 
    " float center_y = height/2.0;" 
    " float x = (gl_FragCoord.x - center_x) * periods_x/width;" 
    " float y = (gl_FragCoord.y - center_y) * periods_y/height;" 
    " gl_FragColor[0] = 0.5 * (1.0 + (sin((pi2 * (sqrt(x*x + y*y) - time)))));\n" 
    " gl_FragColor[1] = 0.0;\n" 
    " gl_FragColor[2] = 0.0;\n" 
    "}\n"; 
static const GLfloat vertices[] = { 
    -1.0, 1.0, 
    1.0, 1.0, 
    1.0, -1.0, 
    -1.0, -1.0, 
}; 
static const GLuint indexes[] = { 
    0, 2, 1, 
    0, 3, 2, 
}; 

double common_get_secs(void) { 
    struct timespec ts; 
    timespec_get(&ts, TIME_UTC); 
    return ts.tv_sec + (1e-9 * ts.tv_nsec); 
} 
const double COMMON_FPS_GRANULARITY_S = 0.5; 
double common_fps_last_time_s; 
unsigned int common_fps_nframes; 
void common_fps_init() { 
    common_fps_nframes = 0; 
    common_fps_last_time_s = common_get_secs(); 
} 
void common_fps_update_and_print() { 
    double dt, current_time_s; 
    current_time_s = common_get_secs(); 
    common_fps_nframes++; 
    dt = current_time_s - common_fps_last_time_s; 
    if (dt > COMMON_FPS_GRANULARITY_S) { 
     printf("FPS = %f\n", common_fps_nframes/dt); 
     common_fps_last_time_s = current_time_s; 
     common_fps_nframes = 0; 
    } 
} 

/* Copy paste. Upstream on OpenGL. */ 
GLint common_get_shader_program(
     const char *vertex_shader_source, 
     const char *fragment_shader_source) { 
    GLchar *log = NULL; 
    GLint fragment_shader, log_length, program, success, vertex_shader; 

    /* Vertex shader */ 
    vertex_shader = glCreateShader(GL_VERTEX_SHADER); 
    glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); 
    glCompileShader(vertex_shader); 
    glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); 
    glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &log_length); 
    log = malloc(log_length); 
    if (log_length > 0) { 
     glGetShaderInfoLog(vertex_shader, log_length, NULL, log); 
     printf("vertex shader log:\n\n%s\n", log); 
    } 
    if (!success) { 
     printf("vertex shader compile error\n"); 
     exit(EXIT_FAILURE); 
    } 

    /* Fragment shader */ 
    fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); 
    glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); 
    glCompileShader(fragment_shader); 
    glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); 
    glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &log_length); 
    if (log_length > 0) { 
     log = realloc(log, log_length); 
     glGetShaderInfoLog(fragment_shader, log_length, NULL, log); 
     printf("fragment shader log:\n\n%s\n", log); 
    } 
    if (!success) { 
     printf("fragment shader compile error\n"); 
     exit(EXIT_FAILURE); 
    } 

    /* Link shaders */ 
    program = glCreateProgram(); 
    glAttachShader(program, vertex_shader); 
    glAttachShader(program, fragment_shader); 
    glLinkProgram(program); 
    glGetProgramiv(program, GL_LINK_STATUS, &success); 
    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); 
    if (log_length > 0) { 
     log = realloc(log, log_length); 
     glGetProgramInfoLog(program, log_length, NULL, log); 
     printf("shader link log:\n\n%s\n", log); 
    } 
    if (!success) { 
     printf("shader link error"); 
     exit(EXIT_FAILURE); 
    } 

    free(log); 
    glDeleteShader(vertex_shader); 
    glDeleteShader(fragment_shader); 

    return program; 
} 

int main(void) { 
    /* SDL variables. */ 
    SDL_Event event; 
    SDL_Window *window; 
    SDL_GLContext gl_context; 
    const unsigned int WINDOW_WIDTH = 500, WINDOW_HEIGHT = WINDOW_WIDTH; 
    double dt, initial_time; 

    /* OpenGL variables. */ 
    GLint 
     attribute_coord2d, 
     ibo_size, 
     width_location, 
     height_location, 
     time_location, 
     periods_x_location, 
     periods_y_location, 
     pi2_location, 
     program 
    ; 
    GLuint ibo, vbo; 
    const char *attribute_name = "coord2d"; 
    const float 
     periods_x = 10.0, 
     periods_y = 10.0, 
     pi2 = 2.0 * acos(-1.0) 
    ; 

    /* SDL init. */ 
    SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO); 
    window = SDL_CreateWindow(__FILE__, 0, 0, 
      WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL); 
    gl_context = SDL_GL_CreateContext(window); 
    glewInit(); 

    /* OpenGL init. */ 
    { 
     program = common_get_shader_program(vertex_shader_source, fragment_shader_source); 
     attribute_coord2d = glGetAttribLocation(program, attribute_name); 
     if (attribute_coord2d == -1) { 
      fprintf(stderr, "error: attribute_coord2d: %s\n", attribute_name); 
      return EXIT_FAILURE; 
     } 
     height_location = glGetUniformLocation(program, "height"); 
     periods_x_location = glGetUniformLocation(program, "periods_x"); 
     periods_y_location = glGetUniformLocation(program, "periods_y"); 
     pi2_location = glGetUniformLocation(program, "pi2"); 
     time_location = glGetUniformLocation(program, "time"); 
     width_location = glGetUniformLocation(program, "width"); 

     glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 
     glUseProgram(program); 
     glViewport(0, 0, WIDTH, HEIGHT); 

     glGenBuffers(1, &vbo); 
     glBindBuffer(GL_ARRAY_BUFFER, vbo); 
     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 

     glGenBuffers(1, &ibo); 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); 
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexes), indexes, GL_STATIC_DRAW); 
     glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &ibo_size); 

     glUniform1f(pi2_location, pi2); 
     glUniform1f(width_location, WIDTH); 
     glUniform1f(height_location, HEIGHT); 
     glUniform1f(periods_x_location, periods_x); 
     glUniform1f(periods_y_location, periods_y); 
    } 

    initial_time = common_get_secs(); 
    common_fps_init(); 
    while (1) { 
     dt = common_get_secs() - initial_time; 

     /* OpenGL draw. */ 
     glClear(GL_COLOR_BUFFER_BIT); 
     glEnableVertexAttribArray(attribute_coord2d); 
     glBindBuffer(GL_ARRAY_BUFFER, vbo); 
     glVertexAttribPointer(attribute_coord2d, 2, GL_FLOAT, GL_FALSE, 0, 0); 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); 
     glUniform1f(time_location, dt); 
     glDrawElements(GL_TRIANGLES, ibo_size/sizeof(indexes[0]), GL_UNSIGNED_INT, 0); 
     glDisableVertexAttribArray(attribute_coord2d); 

     common_fps_update_and_print(); 
     SDL_GL_SwapWindow(window); 
     if (SDL_PollEvent(&event) && event.type == SDL_QUIT) 
      break; 
    } 

    /* OpenGL cleanup. */ 
    glDeleteBuffers(1, &ibo); 
    glDeleteBuffers(1, &vbo); 
    glDeleteProgram(program); 

    /* SDL cleanup. */ 
    SDL_GL_DeleteContext(gl_context); 
    SDL_DestroyWindow(window); 
    SDL_Quit(); 

    return EXIT_SUCCESS; 
} 

Następnie:

gcc -Wall -std=c11 a.c -lSDL2 -lm -lGL -lGLEW 

GitHub upstreams:

Powiązane problemy