2011-12-22 13 views
5

Sto scrivendo un clone di Wolfenstein 3D utilizzando solo core OpenGL 3.3 per l'università e mi sono imbattuto in un po 'di problemi con gli sprite, ovvero farli scalare correttamente in base sulla distanza.Ridimensionamento degli sprite in base alla distanza dalla telecamera

Da quello che posso dire, le versioni precedenti di OGL farebbero effettivamente questo per voi, ma quella funzionalità è stata rimossa, e tutti i miei tentativi di reimplementare hanno portato ad un completo fallimento.

La mia attuale implementazione è percorribile a distanze, non troppo squallide a metà campo e bizzare a distanza ravvicinata.

Il problema principale (penso) è che non ho alcuna comprensione della matematica che sto usando.
La dimensione di destinazione dello sprite è leggermente più grande del viewport, quindi dovrebbe "uscire dall'immagine" appena ci si arriva, ma non è così. Diventa più piccolo e questo mi confonde molto.
Ho registrato un piccolo video di questo, nel caso in cui le parole non siano sufficienti. (Il mio è a destra)

Expected Result Actual Result

Qualcuno può indicarmi dove sto andando male, e spiegare perché?

Codice:
C++

// setup 
glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT); 
glEnable(GL_PROGRAM_POINT_SIZE); 

// Drawing 
glUseProgram(StaticsProg); 
glBindVertexArray(statixVAO); 
glUniformMatrix4fv(uStatixMVP, 1, GL_FALSE, glm::value_ptr(MVP)); 
glDrawArrays(GL_POINTS, 0, iNumSprites); 

Vertex Shader

#version 330 core 

layout(location = 0) in vec2 pos; 
layout(location = 1) in int spriteNum_; 

flat out int spriteNum; 

uniform mat4 MVP; 

const float constAtten = 0.9; 
const float linearAtten = 0.6; 
const float quadAtten = 0.001; 

void main() { 
    spriteNum = spriteNum_; 
    gl_Position = MVP * vec4(pos.x + 1, pos.y, 0.5, 1); // Note: I have fiddled the MVP so that z is height rather than depth, since this is how I learned my vectors. 
    float dist = distance(gl_Position, vec4(0,0,0,1)); 
    float attn = constAtten/((1 + linearAtten * dist) * (1 + quadAtten * dist * dist)); 
    gl_PointSize = 768.0 * attn; 
} 

Fragment Shader

#version 330 core 

flat in int spriteNum; 

out vec4 color; 

uniform sampler2DArray Sprites; 

void main() { 
    color = texture(Sprites, vec3(gl_PointCoord.s, gl_PointCoord.t, spriteNum)); 
    if (color.a < 0.2) 
     discard; 
} 
+0

Perché si usa 'pos.x + 1'? –

+0

Penso che sia perché ha impostato la posizione del centro dello sprite in basso a sinistra, ma vuole davvero il punto di ancoraggio in alto al centro. –

+0

Voglio che il punto di attacco si trovi al centro del bordo inferiore, ma a causa di una serie di problemi di allineamento che non ho ancora risolto, devo spostare un po 'di cose in un modo un po' strano. –

risposta

11

Prima di tutto, non capisco perché utilizzi lo pos.x + 1.

Successivamente, come diceva Nathan, non si dovrebbe usare il punto di clip-spazio, ma il punto dello spazio-occhio. Ciò significa che utilizzi solo il punto trasformato in modelview (senza proiezione) per calcolare la distanza.

uniform mat4 MV;  //modelview matrix 

vec3 eyePos = MV * vec4(pos.x, pos.y, 0.5, 1); 

Inoltre, non comprendo completamente il calcolo dell'attenuazione. Al momento un valore più alto di constAtten significa meno attenuazione.Perché non basta usare il modello che i parametri del punto di deprecate di OpenGL usati:

float dist = length(eyePos); //since the distance to (0,0,0) is just the length 
float attn = inversesqrt(constAtten + linearAtten*dist + quadAtten*dist*dist); 

EDIT: Ma in generale penso che questo modello di attenuazione non è un buon modo, perché spesso si vuole solo lo sprite per mantenere la sua dimensione dello spazio dell'oggetto, che devi abbastanza interferire con i fattori di attenuazione per raggiungere quello che penso.

Un modo migliore è quello di introdurre il relativo dimensione spazio oggetti e solo calcolare la dimensione dello schermo spazio in pixel (che è ciò che gl_PointSize effettivamente) sulla base che utilizzando la visualizzazione corrente e la configurazione di proiezione:

uniform mat4 MV;    //modelview matrix 
uniform mat4 P;     //projection matrix 
uniform float spriteWidth;  //object space width of sprite (maybe an per-vertex in) 
uniform float screenWidth;  //screen width in pixels 

vec4 eyePos = MV * vec4(pos.x, pos.y, 0.5, 1); 
vec4 projCorner = P * vec4(0.5*spriteWidth, 0.5*spriteWidth, eyePos.z, eyePos.w); 
gl_PointSize = screenWidth * projCorner.x/projCorner.w; 
gl_Position = P * eyePos; 

Questo in modo che lo sprite ottenga sempre le dimensioni che avrebbe quando viene rappresentato come un quad strutturato con una larghezza di spriteWidth.

MODIFICA: Naturalmente è anche necessario tenere a mente i limiti degli sprite puntiformi. Un punto sprite è troncato in base alla sua posizione centrale. Ciò significa che quando il suo centro si muove fuori dallo schermo, l'intero sprite scompare. Con sprite di grandi dimensioni (come nel tuo caso, penso) questo potrebbe davvero essere un problema.

Quindi preferirei suggerirti di utilizzare quads semplici strutturati. In questo modo elimini l'intero problema di attenuazione, dato che i quad sono appena trasformati come ogni altro oggetto 3d. È sufficiente implementare la rotazione verso il visualizzatore, che può essere eseguita sulla CPU o nel vertex shader.

+0

Il tuo secondo bit di codice causa [this] (http://i.imgur.com/EvTwW.png), e non sono completamente sicuro di cosa stia facendo. Potresti spiegarlo ulteriormente, per favore? Guarderò anche i quad. –

+0

@Lexi Che intendi, la prima modifica? –

+0

Sì, quello con projCorner. –

1

Non penso di voler basare il calcolo della distanza nel vertex shader sulla posizione proiettata. Calcola invece la posizione relativa alla tua vista, cioè usa la matrice vista modello invece di quella vista modello-vista-proiezione.

Pensateci in questo modo: nello spazio proiettato, mentre un oggetto si avvicina a voi, la sua distanza nelle direzioni orizzontale e verticale diventa esagerata. Puoi vedere questo nel modo in cui le lampade si allontanano dal centro verso la parte superiore dello schermo quando ti avvicini a loro. Quella esagerazione di quelle dimensioni farà diventare la distanza più grande quando ci si avvicina molto, motivo per cui si vede l'oggetto ridursi.

5

Sulla base Christian Rau's answer (ultima modifica), ho implementato un geometry shader che costruisce un cartellone in ViewSpace, che sembra risolvere tutti i miei problemi:

Expected Result Actual Result

Ecco i shaders: (Nota che ho risolto il problema di allineamento che ha richiesto lo shader originale per aggiungere 1 ax)

Vertex Shader

#version 330 core 

layout (location = 0) in vec4 gridPos; 
layout (location = 1) in int spriteNum_in; 

flat out int spriteNum; 

// simple pass-thru to the geometry generator 
void main() { 
    gl_Position = gridPos; 
    spriteNum = spriteNum_in; 
} 

Geometry Shader

#version 330 core 

layout (points) in; 
layout (triangle_strip, max_vertices = 4) out; 

flat in int spriteNum[]; 

smooth out vec3 stp; 

uniform mat4 Projection; 
uniform mat4 View; 

void main() { 
    // Put us into screen space. 
    vec4 pos = View * gl_in[0].gl_Position; 

    int snum = spriteNum[0]; 

    // Bottom left corner 
    gl_Position = pos; 
    gl_Position.x += 0.5; 
    gl_Position = Projection * gl_Position; 
    stp = vec3(0, 0, snum); 
    EmitVertex(); 

    // Top left corner 
    gl_Position = pos; 
    gl_Position.x += 0.5; 
    gl_Position.y += 1; 
    gl_Position = Projection * gl_Position; 
    stp = vec3(0, 1, snum); 
    EmitVertex(); 

    // Bottom right corner 
    gl_Position = pos; 
    gl_Position.x -= 0.5; 
    gl_Position = Projection * gl_Position; 
    stp = vec3(1, 0, snum); 
    EmitVertex(); 

    // Top right corner 
    gl_Position = pos; 
    gl_Position.x -= 0.5; 
    gl_Position.y += 1; 
    gl_Position = Projection * gl_Position; 
    stp = vec3(1, 1, snum); 
    EmitVertex(); 

    EndPrimitive(); 
} 

Fragment Shader

#version 330 core 

smooth in vec3 stp; 

out vec4 colour; 

uniform sampler2DArray Sprites; 

void main() { 
    colour = texture(Sprites, stp); 
    if (colour.a < 0.2) 
     discard; 
} 
+2

Quindi la risposta di Christian Rau è che cosa ti ha portato dove sei, eppure accetti la tua risposta? Wow, è incredibilmente spensierato dopo lo sforzo che ha fatto. Modo di dare le persone per scontate. –

+0

@NickWiggill A dire il vero, non ci avevo proprio pensato. Ho fatto e accettato la mia risposta perché pensavo che sarebbe stato più utile a chiunque altro con il mio problema. –

+0

+1 sulla risposta in cambio della tua considerazione nel riassegnare la risposta accettata. Il tuo sarà probabilmente con più voti comunque. –

0

Almeno in OpenGL ES 2.0, v'è un limite massimo dimensioni dei gl_PointSize imposto dalla implementazione OpenGL. Puoi interrogare le dimensioni con ALIASED_POINT_SIZE_RANGE.

Problemi correlati