Calcolo delle normali

In versioni recenti di GLSL molte funzioni del geometry shader sono state trasferite al Tessellation Shader. In particolare, c'era l'abitudine di usare lo shader per aumentare il numero di vertici dei poligoni in modo dinamico. Si poteva fare, ma ottenerlo tramite un geometry shader che deve emettere un vertice alla volta è inefficiente ed infrange il parallelismo della scheda video.

L'uso del geometry shader è consigliato più che altro per eseguire compiti che hanno la necessità di conoscere un'intera primitiva, non solo i suoi singoli vertici.

Uno di questi compiti è il calcolo delle normali. La normale a un triangolo può essere ottenuta come il prodotto vettoriale tra due lati. Si definisce una funzione ComputeNormal che riceve in ingresso i tre vertici e restituisce la normale.

vec3 ComputeNormal(in vec3 v1,in vec3 v2,in vec3 v3)
  {
  vec3 diff1 = v2 - v1;                            // first edge
  vec3 diff2 = v2 - v3;                            // second edge
  
  vec3 crossproduct = cross(diff1,diff2);

  return normalize(crossproduct);  
  }

Il contenuto del listato sovrastante è rilasciato nel pubblico dominio.

Questo algoritmo è meno efficace di usare le normali calcolate per singolo vertice, se sono disponibili nella geometria originale, perché calcola la stessa normale per tutta la faccia. L'effetto finale è più simile al faceted shading, anche applicando l'illuminazione per pixel.

Siccome si sta inserendo un geometry shader, è necessario rinominare tutte le variabili varying di input al fragment shader. Le variabili create dal vertex shader cominciavano con la v, perciò una convenzione può essere quella di far cominciare quelle del geometry shader con la g:

varying float gmatshininess;
varying vec3 gmatspecular;
varying vec3 geyeposition;
varying vec3 gnormal;

L'unica differenza per il vertex shader è che non deve calcolare la normale:

#version 120

attribute float matshininess;
attribute vec3 matspecular;

varying float vmatshininess;
varying vec3 vmatspecular;

varying vec3 veyeposition;

void main()
  {
  vec4 rp = gl_ModelViewMatrix * gl_Vertex;                   
  veyeposition = rp.xyz / rp.w;                 
  
  gl_FrontColor = gl_Color;
  gl_BackColor = vec4(0.0,0.0,0.0,1.0);
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
  
  vmatspecular = matspecular;
  vmatshininess = matshininess;
  }

Il contenuto del listato sovrastante è rilasciato nel pubblico dominio.

Il geometry shader si limita a copiare verso il fragment shader tutte le variabili, eccetto gnormal, che invece deve calcolare. Il vertex shader fornisce già i vertici in eye coordinates (veyeposition), perciò non è necessaria nessuna moltiplicazione per gl_NormalMatrix.

#version 120
#extension GL_EXT_geometry_shader4: enable

varying out float gmatshininess;
varying out vec3 gmatspecular;
varying out vec3 geyeposition;
varying out vec3 gnormal;

varying in float vmatshininess[3];
varying in vec3 vmatspecular[3];
varying in vec3 veyeposition[3];

vec3 ComputeNormal(in vec3 v1,in vec3 v2,in vec3 v3);

void main()
  {
  gnormal = ComputeNormal(veyeposition[0],veyeposition[1],veyeposition[2]);
  
  for (int i = 0; i < 3; i++)
    {
    gl_FrontColor = gl_FrontColorIn[i];
    gl_BackColor = gl_BackColorIn[i];
    gl_Position = gl_PositionIn[i];
    
    gmatshininess = vmatshininess[i];
    gmatspecular = vmatspecular[i];
    geyeposition = veyeposition[i];
    
    EmitVertex();
    }
    
  EndPrimitive();
  }

Il contenuto del listato sovrastante è rilasciato nel pubblico dominio.

Configurazione per l'esempio

#define SHADER_FOLDER "shaders/normals/"

#define USE_VERTEX_SHADER true
#define USE_FRAGMENT_SHADER true
#define USE_GEOMETRY_SHADER true
#define USE_NORMAL_SHADER true
#define USE_VERTEX_LIGHT_SHADER false
#define USE_FRAGMENT_LIGHT_SHADER true
#define USE_TESSEVAL_LIGHT_SHADER false
#define USE_TESSELLATION_SHADERS false

#define GEOMETRY_INPUT_TYPE GL_TRIANGLES         
#define GEOMETRY_OUTPUT_TYPE GL_TRIANGLE_STRIP   
#define GEOMETRY_MAX_VERTICES 3                

#define ENABLE_BACK_FACE_CULLING true
#define LOAD_NORMALS false
#define TIMER_MS 0

Risultato:

Risultato calcolo delle normali

Copyright © 2011 Riccardo Monica
Eccetto ove espressamente indicato altrimenti, il contenuto di questa pagina è disponibile secondo la licenza Creative Commons Attribuzione - Condividi allo stesso modo 3.0.