個人檔案Quake3 启示录相片部落格清單更多 工具 說明

部落格


7月23日

光照

对于灯光上面的环境光、漫反射、镜面光分别乘以当前材质的环境、漫反射、镜面分量,然后再叠加到顶点颜色上。
对于direct3d 图形与动画程序设计上面的 环境、漫反射、镜面 shader 是相对于方向光的 shader.
如果是点光源还要有衰减,而聚光灯还要有内外夹角。
当然对于light 可以使用 dx 的固定管线灯光,这样可以不用写shader(但受8个灯光的限制)。下面是一个摘录的opengl 的 shader 光照模型。跟directx 在镜面光上可能有些不同


这里提供一种使用GLSL shader实现更多数量的局部光照。
在GLSL里,首先建立光照参数数据结构:

struct myLightParams
{
    bool enabled;
    vec4 position;
    vec4 ambient;
    vec4 diffuse;
    vec4 specular;
    vec3 spotDirection;
    float spotCutoff;
    float spotExponent;
    float constantAttenuation;
    float linearAttenuation;
    float quadraticAttenuation;
};

然后,需要app传入的参数:

const int maxLightCount = 32;
uniform myLightParams light[maxLightCount];
uniform bool bLocalViewer;
uniform bool bSeperateSpecualr;

主函数:

void main()
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    vec4 pos = gl_ModelViewMatrix * gl_Vertex;
    vec3 epos = vec3(pos)/pos.w;

    vec3 normal = normalize(gl_NormalMatrix * gl_Normal);

    vec3 eye;
    if (bLocalViewer)
       eye = -normalize(epos);
   else
        eye = vec3(0, 0, 1.0);

    vec4 amb = vec4(0);
    vec4 diff = vec4(0);
    vec4 spec = vec4(0);

for (int i=0; i<maxLightCount; i++)
{
     if (light[i].enabled == false)
       continue;

     if (light[i].position.w == 0)
    {
            DirectionalLight(i, eye, epos, normal, amb, diff, spec);
    }
    else if (light[i].spotCutoff == 180.0)
    {
            PointLight(i, eye, epos, normal, amb, diff, spec);
    }
    else
    {
            SpotLight(i, eye, epos, normal, amb, diff, spec);
    }
}

    vec4 color = gl_FrontLightModelProduct.sceneColor +
                 amb * gl_FrontMaterial.ambient +
                 diff * gl_FrontMaterial.diffuse;

if (bSeperateSpecualr)
{
        gl_FrontSecondaryColor = spec * gl_FrontMaterial.specular;
}
else
{
        gl_FrontSecondaryColor = vec4(0, 0, 0, 1.0);
        color += spec * gl_FrontMaterial.specular;
}

    gl_FrontColor = color;
}

//对于方向光源的计算:

void DirectionalLight(int i, vec3 eye, vec3 epos, vec3 normal, inout vec4 amb, inout vec4 diff, inout vec4 spec)
{
     float dotVP = max(0, dot(normal, normalize(vec3(light[i].position))));
     float dotHV = max(0, dot(normal, normalize(eye+normalize(vec3(light[i].position)))));

    amb += light[i].ambient;
    diff += light[i].diffuse * dotVP;
    spec += light[i].specular * pow(dotHV, gl_FrontMaterial.shininess);
}

//对于点光源:

void PointLight(int i, vec3 eye, vec3 epos, vec3 normal, inout vec4 amb, inout vec4 diff, inout vec4 spec)
{
    vec3 VP = vec3(light[i].position) - epos;
    float d = length(VP);
    VP = normalize(VP);

    float att = 1.0/(light[i].constantAttenuation + light[i].linearAttenuation*d + light[i].quadraticAttenuation*d*d);
    vec3 h = normalize(VP+eye);

    float dotVP = max(0, dot(normal, VP));
    float dotHV = max(0, dot(normal, h));

    amb += light[i].ambient * att;
    diff += light[i].diffuse * dotVP * att;
    spec += light[i].specular * pow(dotHV, gl_FrontMaterial.shininess) * att;
}

//对于聚光灯:

void SpotLight(int i, vec3 eye, vec3 epos, vec3 normal, inout vec4 amb, inout vec4 diff, inout vec4 spec)
{
    vec3 VP = vec3(light[i].position) - epos;
    float d = length(VP);
    VP = normalize(VP);

    float att = 1.0/(light[i].constantAttenuation + light[i].linearAttenuation*d + light[i].quadraticAttenuation*d*d);

    float dotSpot = dot(-VP, normalize(light[i].spotDirection));
    float cosCutoff = cos(light[i].spotCutoff*3.1415926/180.0);

    float spotAtt = 0;
    if (dotSpot < cosCutoff)
        spotAtt = 0;
    else
        spotAtt = pow(dotSpot, light[i].spotExponent);

    att *= spotAtt;

    vec3 h = normalize(VP+eye);

    float dotVP = max(0, dot(normal, VP));
    float dotHV = max(0, dot(normal, h));

    amb += light[i].ambient * att;
    diff += light[i].diffuse * dotVP * att;
    spec += light[i].specular * pow(dotHV, gl_FrontMaterial.shininess) * att;
}

这样,对于场景之中的任意对象,它所能够接受计算的光源就可以突破8个的限制了。
上述光照计算是遵循OpenGL spec的,因此与固定管线的效果是一致的。

回應

請稍候...
很抱歉,您輸入的回應過長。請縮短您的回應。
您尚未輸入內容,請再試一次。
很抱歉,目前無法新增您的回應,請稍後再試。
若要新增回應,您的父母必須先給您權限。要求權限
您的家長已關閉回應功能。
很抱歉,目前無法刪除您的回應,請稍後再試。
您已超過每日回應上限次數,請於 24 小時後再試一次。
由於系統顯示您可能傳送垃圾郵件給其他使用者,因此您帳號中的回應功能已遭停用。 如果您認為自己帳號遭錯誤停用,請連絡 Windows Live 支援
請完成下列安全檢查,以完成回應。
您輸入的安全檢查字元必須與圖片或音訊中的字元相符。

若要新增回應,請以您的 Windows Live ID 登入 (若您使用 Hotmail、Messenger 或 Xbox LIVE,則您已擁有 Windows Live ID)。登入


沒有 Windows Live ID?註冊

引用通告

此內容的引用通告是:
http://topameng.spaces.live.com/blog/cns!F962D4854A8233D!470.trak
引述這則內容的部落格