Универсальный шейдер освещения GLSL - PullRequest
17 голосов
/ 20 марта 2012

Освещение на основе пикселей является распространенной проблемой во многих приложениях OpenGL, поскольку стандартное освещение OpenGL имеет очень низкое качество.

Я хочу использовать программу GLSL, чтобы вместо освещения на основе пикселей в моей программе OpenGLиз каждой вершины.Просто диффузное освещение, но с туманом, текстурой и текстурой-альфа как минимум.

Я начал с этого шейдера :

texture.vert:

varying vec3 position;
varying vec3 normal; 

void main(void) 
{
  gl_Position       = gl_ModelViewProjectionMatrix * gl_Vertex;
  gl_FrontColor     = gl_Color;
  gl_TexCoord[0]    = gl_MultiTexCoord0; 
  normal        = normalize(gl_NormalMatrix * gl_Normal);
  position      = vec3(gl_ModelViewMatrix * gl_Vertex);
}

texture.frag:

uniform sampler2D Texture0;
uniform int ActiveLights;

varying vec3 position;
varying vec3 normal; 

void main(void) 
{
  vec3 lightDir;
  float  attenFactor;
  vec3 eyeDir           = normalize(-position); // camera is at (0,0,0) in ModelView space
  vec4 lightAmbientDiffuse  = vec4(0.0,0.0,0.0,0.0);
  vec4 lightSpecular        = vec4(0.0,0.0,0.0,0.0);    

  // iterate all lights
  for (int i=0; i<ActiveLights; ++i)
  {
    // attenuation and light direction
    if (gl_LightSource[i].position.w != 0.0)
    {
        // positional light source
        float dist  = distance(gl_LightSource[i].position.xyz, position);
        attenFactor = 1.0/( gl_LightSource[i].constantAttenuation + 
                    gl_LightSource[i].linearAttenuation * dist +
                    gl_LightSource[i].quadraticAttenuation * dist * dist );
        lightDir    = normalize(gl_LightSource[i].position.xyz - position);
    }       
    else 
    {           
        // directional light source         
        attenFactor = 1.0;          
        lightDir    = gl_LightSource[i].position.xyz;       
    }       
    // ambient + diffuse        
    lightAmbientDiffuse     += gl_FrontLightProduct[i].ambient*attenFactor;     
    lightAmbientDiffuse     += gl_FrontLightProduct[i].diffuse * max(dot(normal, lightDir), 0.0) * attenFactor; 
    // specular     
    vec3 r      = normalize(reflect(-lightDir, normal));
    lightSpecular   += gl_FrontLightProduct[i].specular * 
                  pow(max(dot(r, eyeDir), 0.0), gl_FrontMaterial.shininess) *
                  attenFactor;  
  }     
  // compute final color    
  vec4 texColor = gl_Color * texture2D(Texture0, gl_TexCoord[0].xy);    
  gl_FragColor  = texColor * (gl_FrontLightModelProduct.sceneColor + lightAmbientDiffuse) + lightSpecular;

  float fog = (gl_Fog.end - gl_FogFragCoord) * gl_Fog.scale;    // Intensität berechnen 
  fog       = clamp(fog, 0.0, 1.0);                 // Beschneiden 
  gl_FragColor  = mix(gl_Fog.color, gl_FragColor, fog);         // Nebelfarbe einmischen 
}

Комментарии немецкие, потому что это немецкий сайт, на котором был опубликован этот код, извините.

Но все, что делает этот шейдер, это делает все очень темным.Никаких световых эффектов - шейдерные коды компилируются.Если я использую только GL_LIGHT0 в шейдере фрагментов, то это, кажется, работает, но только для полигонов, обращенных к камере, и мой полигон на полу просто очень темный.Также квады с текстурами RGBA не показывают никаких признаков прозрачности.Я использую стандартный glRotate / Translate для матрицы ModelView и glVertex / Normal для моих полигонов.Освещение OpenGL прекрасно работает, за исключением того факта, что оно выглядит некрасиво на очень больших поверхностях.Я трижды проверил свои нормали, они в порядке.

Что-то не так в приведенном выше коде?ИЛИ Скажите, почему для этой задачи не существует универсального шейдера освещения (точечный свет с уменьшением расстояния: свеча, если хотите) - разве не должен быть только один правильный способ сделать это?Я не хочу удар / нормальный / параллакс / toon / blur / любые эффекты.Я просто хочу, чтобы мое освещение работало лучше с более крупными полигонами.

Все найденные мною учебные пособия полезны только для освещения одного объекта, когда камера находится на уровне 0,0,0 лицом, перпендикулярным к объекту.Выше приведено только то, что, по крайней мере, похоже на то, что я хочу сделать.

1 Ответ

12 голосов
/ 14 июля 2012

Я настоятельно рекомендую вам прочитать эту статью , чтобы увидеть, как стандартная молния ADS выполняется в GLSL. Это GL 4.0, но не проблема для адаптации к вашей версии:

Кроме того, вы работаете в пространстве вида (камеры), так что НЕ "T отрицает вектор глаз:

    vec3 eyeDir  = normalize(-position); 

У меня были довольно похожие проблемы с вашими, потому что я также отрицал вектор глаз, забывая, что он преобразуется в пространство вида. Ваши диффузные и зеркальные вычисления тоже кажутся неправильными в текущем сценарии. На вашем месте я бы не стал использовать данные из фиксированного конвейера вообще, иначе какой смысл делать это в шейдере? Вот метод для расчета рассеянного и зеркального в точечной молнии ADS на фрагмент:

void ads( int lightIndex,out vec3 ambAndDiff, out vec3 spec )
{


vec3 s =  vec3(lights[lightIndex].Position -  posOut) ;
vec3 v = normalize( posOut.xyz );
vec3 n = normalize(normOut); 
vec3 h = normalize(v+s) ;// half vector (read in the web on what it is )


    vec3 diffuse =  ((Ka+ lights[lightIndex].Ld) * Kd *  max( 0.0,dot(n, v) )) ;
     spec =  Ks *  pow( max(0.0, dot(n,h) ), Shininess ) ;  


 ambAndDiff =   diffuse ;

/// Ka-material ambient factor
/// Kd-material diffuse factor
/// Ks-material specular factor.
/// lights[lightIndex].Ld-lights diffuse factor;you may also add La and Ls if you want to have even more control of the light shading.
}

Кроме того, я бы не советовал вам использовать уравнение ослабления, которое у вас здесь есть, это сложно контролировать. Если вы хотите добавить ослабление на основе радиуса света есть этот хороший пост в блоге:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...