在OpenGL ES2着色器中计算顶点法线

时间:2018-02-15 19:51:24

标签: android opengl-es-2.0

我在使用Google Poly抓取的obj模型的照明方面遇到了一些问题。在这个模型中,定义了一堆没有法线的面。因此,我用于漫反射颜色的计算返回0.有没有办法估计法线如果没有提供或我需要解析obj并手动注入法线?

这是我的片段着色器:

precision mediump float;

uniform bool u_UseTexAmbient;
uniform bool u_UseTexDiffuse;
uniform bool u_UseTexSpecular;

uniform sampler2D u_TexAmbient;
uniform sampler2D u_TexDiffuse;
uniform sampler2D u_TexSpecular;

uniform vec3 u_MtlAmbient;
uniform vec3 u_MtlDiffuse;
uniform vec3 u_MtlSpecular;

uniform float u_D;
uniform float u_SpecularPower;

uniform vec4 u_LightingParameters;

varying vec3 v_ViewPosition;
varying vec3 v_ViewNormal;
varying vec2 v_TexCoord;

vec4 getColor(bool useTex, sampler2D tex, vec3 mtl);
vec3 computeDiffuse(vec3 viewNormal, vec3 rawDiffuseColor);
vec3 computeSpecular(vec3 viewNormal, vec3 viewFragmentDirection, vec3 rawSpecularColor);

void main() {
    const float kGamma = 0.4545454;
    vec3 viewFragmentDirection = normalize(v_ViewPosition);
    vec3 viewNormal = normalize(v_ViewNormal);

    vec4 rawAmbientColor = getColor(u_UseTexAmbient, u_TexAmbient, u_MtlAmbient);
    vec4 rawDiffuseColor = getColor(u_UseTexDiffuse, u_TexDiffuse, u_MtlDiffuse);
    vec4 rawSpecularColor = getColor(u_UseTexSpecular, u_TexSpecular, u_MtlSpecular);

    vec3 diffuse = computeDiffuse(viewNormal, rawDiffuseColor.rgb);
    vec3 specular = computeSpecular(viewNormal, viewFragmentDirection, rawSpecularColor.rgb);

    gl_FragColor.a = rawDiffuseColor.a * u_D;
    gl_FragColor.rgb = pow(rawAmbientColor.rgb + diffuse + specular, vec3(kGamma));
}

vec4 getColor(bool useTex, sampler2D tex, vec3 mtl) {
    const float kInverseGamma = 2.2;
    vec4 color;
    if (useTex) {
        color = texture2D(tex, vec2(v_TexCoord.x, 1.0 - v_TexCoord.y));
    } else {
        color = vec4(mtl.r, mtl.g, mtl.b, 1.0);
    }
    color.rgb = pow(color.rgb, vec3(kInverseGamma));
    return color;
}

vec3 computeDiffuse(vec3 viewNormal, vec3 rawDiffuseColor) {
    vec3 diffuse = u_LightingParameters.w * rawDiffuseColor
            * max(dot(u_LightingParameters.xyz, viewNormal), 0.0);
    return clamp(diffuse, 0.0, 1.0);
}

vec3 computeSpecular(vec3 viewNormal, vec3 viewFragmentDirection, vec3 rawSpecularColor) {
    vec3 reflectedLightDirection = reflect(u_LightingParameters.xyz, viewNormal);
    float specularStrength = max(0.0, dot(viewFragmentDirection, reflectedLightDirection));
    vec3 specular = u_LightingParameters.w * rawSpecularColor *
        pow(specularStrength, u_SpecularPower);
    return clamp(specular, 0.0, 1.0);
}

这个顶点着色器取自Google的ARCore示例:

uniform mat4 u_ModelView;
uniform mat4 u_ModelViewProjection;

attribute vec4 a_Position;
attribute vec3 a_Normal;
attribute vec2 a_TexCoord;

varying vec3 v_ViewPosition;
varying vec3 v_ViewNormal;
varying vec2 v_TexCoord;

void main() {
    v_ViewPosition = (u_ModelView * a_Position).xyz;
    v_ViewNormal = normalize((u_ModelView * vec4(a_Normal, 0.0)).xyz);
    v_TexCoord = a_TexCoord;
    gl_Position = u_ModelViewProjection * a_Position;
}

基本上我所看到的是某些模型无法为我提供a_Normal,因此我希望能够以某种方式在顶点着色器中生成v_ViewNormal。这甚至可能吗?

以下是完整模型的链接:https://poly.google.com/view/eydI4__jXpi

1 个答案:

答案 0 :(得分:0)

通过使用偏导数函数dFdx, dFdy

,可能会在片段着色器中对每个片段的法向量进行calucalte

请注意,在OpenGL ES 2.0中,OES_standard_derivatives扩展名是必需的,因为衍生函数只是自OpenGL ES 3.0以来才成为标准。

请参阅OpenGL ES Shading Language 3.00 Specification; 8.9 Fragment Processing Functions; page 104

  

genType dFdx (genType p)

     

使用输入参数p的局部差分返回 x 中的导数。

     

genType dFdy (genType p)

     

使用输入参数p的局部差分返回 y 中的导数。


代码可能看起来像这样:

#extension GL_OES_standard_derivatives : enable

varying vec3 view_pos;

void main
{
    vec3 dX     = dFdx(view_pos);
    vec3 dY     = dFdy(view_pos);
    vec3 normal = normalize(cross(dX, dY));

    ....
}


请参阅An introduction to shader derivative functions - Face normal computation (flat shader)