2017-01-30 3 views
0

Je prends un cours en WebGL chez NTNU. J'explore actuellement ce que les shaders font et comment je peux les utiliser.Est-il possible d'exécuter du code une fois par appel draw dans WebGL?

Un exemple que nous avons nous montre que nous calculons une matrice de projection, puis la positionnons dans le vertex shader, puis faisons un appel draw. Je voulais essayer de faire ce calcul matriciel dans un shader.

Cela signifie que je dois placer le code ailleurs que dans la fonction main() dans le vertex shader, puisque celui-ci est invoqué plusieurs fois par appel draw.

Vertex shader:

uniform vec3 camRotation; 
attribute vec3 position; 
void main() { 

    // I want this code to run only once per draw call 
    float rX = camRotation[0]; 
    float rY = camRotation[1]; 
    float rZ = camRotation[2]; 
    mat4 camMatrix = mat4(
     cos(rY) * cos(rZ), cos(rZ) * sin(rX) * sin(rY) - cos(rX) * sin(rZ), sin(rX) * sin(rZ) + cos(rX) * cos(rZ) * sin(rY), 0, // 
     cos(rY) * sin(rZ), cos(rX) * cos(rZ) + sin(rX) * sin(rY) * sin(rZ), cos(rX) * sin(rY) * sin(rZ) - cos(rZ) * sin(rX), 0, // 
     -sin(rY), cos(rY) * sin(rX), cos(rX) * cos(rY), 0, // 
     0, 0, 0, 1 
    ); 
    // End of code in question 

    gl_Position = camMatrix * vec4(position, 1); 
    gl_PointSize = 5.0; 
} 

Est-il possible? Suis-je un imbécile pour essayer?

+0

Vous pourriez trouver [ces articles utiles] (http://webglfundamentals.org) – gman

Répondre

3

AFAIK, il n'y a aucun moyen de le faire. Vous devez calculer camMatrix dans votre code JS et le transmettre au shader via uniforme:

uniform mat4 camMatrix; 
attribute vec3 position; 
void main() { 
    gl_Position = camMatrix * vec4(position, 1); 
    gl_PointSize = 5.0; 
} 

Maintenant, vous devez calculer la matrice de JS:

// assuming that program is your compiled shader program and 
// gl is your WebGL context. 
const cos = Math.cos; 
const sin = Math.sin; 
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'camMatrix'), [ 
    cos(rY) * cos(rZ), cos(rZ) * sin(rX) * sin(rY) - cos(rX) * sin(rZ), sin(rX) * sin(rZ) + cos(rX) * cos(rZ) * sin(rY), 0, 
    cos(rY) * sin(rZ), cos(rX) * cos(rZ) + sin(rX) * sin(rY) * sin(rZ), cos(rX) * sin(rY) * sin(rZ) - cos(rZ) * sin(rX), 0, 
    -sin(rY), cos(rY) * sin(rX), cos(rX) * cos(rY), 0, 
    0, 0, 0, 1 
]); 
+0

Merci pour la réponse. Je suis conscient de la méthode que vous fournissez, je suis curieux de voir si je peux faire ces calculs (comme 'cos (rY)') dans le shader une fois par appel tirage – gromit190

+1

@Birger Je serais très surpris à cause de la façon dont GPU exécute les shaders. Décrire grossièrement chaque exécution d'un shader (pour chaque sommet, si nous parlons de vertex shaders, et pour chaque pixel dans le cas de fragment shaders) obtient son propre état et un morceau de mémoire. Et la synchronisation et l'échange de données entre eux seraient quelque peu lourds et potentiellement lents. –

2

Non ce pas possible, le concept de shaders doit être vectorisable pour pouvoir fonctionner en parallèle. Même si vous pouviez y avoir, il n'y aurait pas beaucoup de gain car l'avantage de la vitesse des GPU est (en plus d'autres choses) intrinsèquement basé sur sa capacité à faire des calculs en parallèle. Cela mis à part, vous avez généralement une matrice de projection de vue combinée qui reste statique pendant tous les appels de tirage (d'une image) et une matrice modèle/monde attachée à chaque objet que vous dessinez. La matrice de projection fait ce que son nom implique en projetant les points de façon perspective ou orthogonale (vous pouvez considérer ceci comme la lentille de votre caméra). La matrice de vue est une transformation permettant de traduire/faire pivoter cette projection (position et orientation de la caméra) alors que la matrice monde/modèle par objet contient les transformations (translation, rotation et échelle) de l'objet individuel.

Dans votre shaders vous transformez votre position de sommet à l'espace mondial en utilisant le modèle/matrice mondiale par objet, puis transformez finalement à l'espace de la caméra en utilisant la matrice ViewProjection prémultipliée:

gl_Position = matViewProjection * (matWorld * vPosition) 

Comme vous En tirant des points en fonction de votre utilisation, vous pourriez réduire la matrice du monde à un simple vecteur de traduction.

+0

C'est très instructif, merci! – gromit190