OpenGL ES3阴影贴图问题 [英] OpenGL ES3 Shadow map problems

查看:110
本文介绍了OpenGL ES3阴影贴图问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用OpenGL ES3在Android上的C ++项目上工作,因此我尝试使用定向光来实现阴影贴图,我对理论理解得很好,但从未成功渲染它. 首先,我创建包含深度图的帧缓冲区:

I work on C++ project for Android with OpenGL ES3, so I try to implement the shadow map with directional light, I understand the theory well but I never get it successfully rendered. first I create the framebuffer which contains the depth map:

glGenFramebuffers(1, &depthMapFBO);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffers(1, GL_NONE);
glReadBuffer(GL_NONE);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

然后,我创建一个着色器程序,该程序将编译深度着色器,如下所示:

then I create a shader program that compiles the depth shader which is the following:

#version 300 es
precision mediump float;

layout (location = 0) in vec3 position;
layout (location = 4) in ivec4 BoneIDs;
layout (location = 5) in vec4 Weights;

const int MAX_BONES = 100;

uniform mat4 lightSpaceMatrix;
uniform mat4 model;
uniform bool skinned;
uniform mat4 gBones[MAX_BONES];

void main(){
vec4 nPos;
if(skinned){
    mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
    BoneTransform     += gBones[BoneIDs[1]] * Weights[1];
    BoneTransform     += gBones[BoneIDs[2]] * Weights[2];
    BoneTransform     += gBones[BoneIDs[3]] * Weights[3];
    nPos=BoneTransform * vec4(position, 1.0);
}
else 
    nPos = vec4(position, 1.0);

vec4 p=model * nPos;
gl_Position = lightSpaceMatrix * p;
}

并使用此着色器程序通过以下步骤绘制场景以及光空间矩阵:

and draw the scene using this shader program with the light space matrix using the following:

glCullFace(GL_FRONT);
        double delta = GetCurrentTime() - firstFrame;
        glm::mat4 camInv = glm::inverse(camera->getViewMatrix());
        glm::mat4 lightSpaceProjection = glm::ortho(-40.0f, 40.0f, -40.0f, 40.0f, -1.0f, 100.0f);
        glm::mat4 lightSpaceView = glm::lookAt(sun->direction, glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));
        lightSpaceMatrix = lightSpaceProjection * (lightSpaceView*camInv) ;
        glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
        glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);

        glClear(GL_DEPTH_BUFFER_BIT);
        directDepthShader.use();
        glUniformMatrix4fv(glGetUniformLocation(directDepthShader.getProgramID(), "lightSpaceMatrix"), 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix));
        for (mesh_it it = castShadowMeshes.begin(); it != castShadowMeshes.end(); it++) {
            it->get()->renderDepth(directDepthShader, delta);
        }
        glCullFace(GL_BACK);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

最后,我使用常规的着色器程序渲染场景,并使用以下代码将深度图绑定到shadowMap制服:

finally I render the scene with the regular shader program and bind the depth map to the shadowMap uniform with the following code:

glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
phongShader.use();
if (sun != nullptr)
    if (sun->castShadow)
        glUniformMatrix4fv(glGetUniformLocation(phongShader.getProgramID(), "lightSpaceMatrix"), 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix));
this->setLightsUniforms(phongShader);
this->setViewUniforms(phongShader);
for (mesh_it it = phongMeshes.begin(); it != phongMeshes.end(); it++) {
    if (it->get()->hasNormalMap) {
        glUniform1i(glGetUniformLocation(phongShader.getProgramID(), "has_normal_map"), 1);
        if (directlights.size() > 0) {
            for (dlight_it it = this->directlights.begin(); it != this->directlights.end(); ++it) {
                GLuint directLightPosLoc = glGetUniformLocation(phongShader.getProgramID(), (const GLchar*) ("directLightPos[" + ToString((*it)->index) + "]").c_str());
                glUniform3f(directLightPosLoc, (*it)->direction.x, (*it)->direction.y, (*it)->direction.z);
            }
        }
        if (pointlights.size() > 0) {
            for (plight_it it = this->pointlights.begin(); it != this->pointlights.end(); ++it) {
                GLuint pointLightPosLoc = glGetUniformLocation(phongShader.getProgramID(), (const GLchar*) ("pointLightPos[" + ToString((*it)->index) + "]").c_str());
                glUniform3f(pointLightPosLoc, (*it)->position.x, (*it)->position.y, (*it)->position.z);
            }
        }
        if (spotlights.size() > 0) {
            for (slight_it it = this->spotlights.begin(); it != this->spotlights.end(); ++it) {
                GLuint spotLightPosLoc = glGetUniformLocation(phongShader.getProgramID(), (const GLchar*) ("spotLightPos[" + ToString((*it)->index) + "]").c_str());
                glUniform3f(spotLightPosLoc, (*it)->position.x, (*it)->position.y, (*it)->position.z);
            }
        }
    }
    double first = GetCurrentTime() - firstFrame;
    it->get()->textures = 0;
    if (sun != nullptr)
        if (sun->castShadow) {
            glUniform1i(glGetUniformLocation(phongShader.getProgramID(), "shadowMap"), it->get()->textures);
            glActiveTexture(GL_TEXTURE0 + it->get()->textures);
            glBindTexture(GL_TEXTURE_2D, depthMap);
            it->get()->textures++;
        }
    it->get()->Render(phongShader, first, deltaTime);
    glBindTexture(GL_TEXTURE_2D, 0);
}

最终,着色器的顶点和片段如下:

finally the shader vertex and fragment are the following:

顶点:

#version 300 es
precision mediump float;

#define NR_DIRECT_LIGHTS 0
#define NR_POINT_LIGHTS 0
#define NR_SPOT_LIGHTS 0

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoord;
layout (location = 3) in vec3 tangent;
layout (location = 4) in ivec4 BoneIDs;
layout (location = 5) in vec4 Weights;

const int MAX_BONES = 100;

out vec2 TexCoords;
out vec3 Normal;
out vec3 tDirectLightPos[NR_DIRECT_LIGHTS];
out vec3 tPointLightPos[NR_POINT_LIGHTS];
out vec3 tSpotLightPos[NR_SPOT_LIGHTS];
out vec3 tViewPos;
out vec3 tFragPos;
out vec4 FragPosLightSpace;

// conditions //
uniform bool has_normal_map;    
uniform bool skinned;
//

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec3 viewPos;
uniform mat4 lightSpaceMatrix;  
uniform mat4 gBones[MAX_BONES];

uniform vec3 directLightPos[NR_DIRECT_LIGHTS];
uniform vec3 pointLightPos[NR_POINT_LIGHTS];
uniform vec3 spotLightPos[NR_SPOT_LIGHTS];

void main(){    
    TexCoords = texCoord;
    vec4 nPos;
    vec3 N=transpose(inverse(mat3(model))) * normal;
    if(skinned){
        mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
        BoneTransform     += gBones[BoneIDs[1]] * Weights[1];
        BoneTransform     += gBones[BoneIDs[2]] * Weights[2];
        BoneTransform     += gBones[BoneIDs[3]] * Weights[3];
        nPos=BoneTransform * vec4(position, 1.0);   
        Normal=(BoneTransform*vec4(N,0.0)).xyz; 
    }
    else{
        nPos = vec4(position, 1.0);
        Normal=N;   
    }
    gl_Position = projection*view * model * nPos;

    vec3 FragPos = vec3(model * nPos);  
    if(has_normal_map){
        mat3 normalMatrix = transpose(inverse(mat3(model)));        
        vec3 T = normalize(normalMatrix * tangent);
        vec3 N = normalize(N);
        T = normalize(T - dot(T, N) * N);
        vec3 B = cross(N,T);    
        if (dot(cross(N, T), B) < 0.0)
           T = T * -1.0;    
        mat3 TBN = transpose(mat3(T, B, N));

        tViewPos=TBN*viewPos;
        tFragPos=TBN*FragPos;
        for(int i = 0; i < NR_DIRECT_LIGHTS-2; i++)
            tDirectLightPos[i]=TBN*directLightPos[i];
        for(int i = 0; i < NR_POINT_LIGHTS-2; i++)
            tPointLightPos[i]=TBN*pointLightPos[i];
        for(int i = 0; i < NR_SPOT_LIGHTS-2; i++)
            tSpotLightPos[i]=TBN*spotLightPos[i];
    }
    else{
        tViewPos=viewPos;
        tFragPos=FragPos;
    }
    FragPosLightSpace = lightSpaceMatrix * vec4(FragPos,1.0);       
}

片段:

#version 300 es
precision mediump float;

#define NR_DIRECT_LIGHTS 0
#define NR_POINT_LIGHTS 0
#define NR_SPOT_LIGHTS 0

out vec4 glFragColor;

vec2 poissonDisk[4] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 )
);
struct SpotLight{
    vec3 position;
    vec3 direction;
    vec3 color;

    float constant;
    float linear;
    float quadratic;
    float cutoff;
    float outerCutOff;
    float intensity;
    int castShadow;
};

struct PointLight{
    vec3 position;
    vec3 color;

    float constant;
    float linear;
    float quadratic;
    float intensity;
};

struct DirectLight {
    vec3 direction;
    vec3 color;
    float intensity;
    int castShadow;
};

in vec2 TexCoords;
in vec3 Normal;
in vec4 FragPosLightSpace;
in vec3 tDirectLightPos[NR_DIRECT_LIGHTS];
in vec3 tPointLightPos[NR_POINT_LIGHTS];
in vec3 tSpotLightPos[NR_SPOT_LIGHTS];
in vec3 tViewPos;
in vec3 tFragPos;

uniform bool Has_normal_map;

uniform sampler2D mat_diffuse;
uniform sampler2D mat_specular;
uniform sampler2D mat_normal;
uniform sampler2D shadowMap;

uniform vec3 matDiffuse;
uniform vec3 matSpecular;
uniform float shininess;

uniform float far_plane;

uniform DirectLight directLights[NR_DIRECT_LIGHTS];
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform SpotLight spotLights[NR_SPOT_LIGHTS];

vec3 calcDirectLight(DirectLight,vec3,vec3,vec3,vec3);
vec3 calcPointLight(PointLight,vec3,vec3,vec3,vec3);
vec3 calcSpotLight(SpotLight,vec3,vec3,vec3,vec3);
float directShadowCalculation();

void main(){
    vec3 normal;
    if(Has_normal_map){
        normal=texture(mat_normal, TexCoords).rgb;
        normal = normalize(normal * 2.0 - 1.0);  // this normal is in tangent space
    }
    else
        normal=normalize(Normal);

    vec3 diffColor= matDiffuse+vec3(texture(mat_diffuse, TexCoords));   
    vec3 specColor= matSpecular+vec3(texture(mat_specular,TexCoords));

    vec3 result;
    result=vec3(0.0);
    for(int i = 0; i < NR_DIRECT_LIGHTS-2; i++)
        result += calcDirectLight(directLights[i],normal,tDirectLightPos[i],diffColor,specColor);
    for(int i = 0; i < NR_POINT_LIGHTS-2; i++)
        result += calcPointLight(pointLights[i],normal,tPointLightPos[i],vec3(0.0,0.2,0.4),specColor);
    for(int i = 0; i < NR_SPOT_LIGHTS-2; i++)
        result += calcSpotLight(spotLights[i],normal,tSpotLightPos[i],diffColor,specColor);
    vec4 color =vec4(result,1.0);   
    float gamma = 2.2;
    color.rgb = pow(color.rgb, vec3(1.0/gamma));
    vec4 ambient=vec4(0.2,0.2,0.2,1.0)*vec4(diffColor,1.0);
    glFragColor=ambient+color;
}

vec3 calcDirectLight(DirectLight light,vec3 norm,vec3 tLightPos,vec3 diffColor,vec3 specColor){ 
    vec3 lightDir ;
    if(Has_normal_map)
        lightDir= normalize(tLightPos);
    else
        lightDir = normalize(light.direction);

    float diff = max(dot(lightDir,norm), 0.0);
    vec3 diffuse = light.color * diff *diffColor;

    vec3 viewDir = normalize(tViewPos- tFragPos);       
    vec3 halfwayDir = normalize(lightDir + viewDir);
    float spec = pow(max(dot(norm, halfwayDir), 0.0), 32.0);
    vec3 specular = shininess* spec *specColor* light.color;
    vec3 result;
    if(light.castShadow==1){
        float shadow = directShadowCalculation();
        result =light.intensity* ( shadow* (diffuse + specular));
    }
    else
        result =light.intensity* (diffuse + specular);
    return result;
}

vec3 calcPointLight(PointLight light,vec3 norm,vec3 tLightPos,vec3 diffColor,vec3 specColor){
    vec3 lightDir ;
    if(Has_normal_map)
        lightDir= normalize(tLightPos-tFragPos);
    else
        lightDir = normalize(light.position - tFragPos);
    float diff = max(dot(lightDir,norm), 0.0);
    vec3 diffuse = light.color * diff * diffColor;

    vec3 viewDir = normalize(tViewPos- tFragPos);
    vec3 halfwayDir = normalize(lightDir + viewDir);
    float spec = pow(max(dot(norm, halfwayDir), 0.0), 16.0);        
    vec3 specular =shininess* specColor * spec * light.color;

    vec3 result;
    float distance = length(light.position - tFragPos);
    float attenuation = 1.0f / (light.constant + light.linear * distance +light.quadratic * (distance * distance));
    diffuse *= attenuation;
    specular *= attenuation;
    result=light.intensity*(diffuse+specular);
    return result;
}

vec3 calcSpotLight(SpotLight light,vec3 norm,vec3 tLightPos,vec3 diffColor,vec3 specColor){                   
    vec3 lightDir ;
    if(Has_normal_map)
        lightDir= normalize(tLightPos-tFragPos);
    else
        lightDir = normalize(light.position - tFragPos);
    float diff = max(dot(lightDir,norm), 0.0);
    vec3 diffuse = light.color * diff * diffColor;

    vec3 viewDir = normalize(tViewPos- tFragPos);

    float spec =0.0;
    vec3 halfwayDir = normalize(lightDir + viewDir);
    spec = pow(max(dot(norm, halfwayDir), 0.0), 16.0);    
    vec3 specular = shininess* light.color * spec * specColor;

    // Spotlight (soft edges)
    float theta = dot(lightDir, normalize(-light.direction)); 
    float epsilon = (light.cutoff - light.outerCutOff);
    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
    diffuse  *= intensity;
    specular *= intensity;

    // Attenuation
    float distance    = length(light.position - tFragPos);
    float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    
    diffuse  *= attenuation;
    specular *= attenuation;   

    vec3 result = intensity*(diffuse+specular);
    return result;
}

float directShadowCalculation(){
    vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
    projCoords = projCoords * 0.5 + 0.5;
    float shadow = 1.0; 
    for (int i=0;i<4;i++){
        if ( texture( shadowMap, -projCoords.xy + poissonDisk[i]/700.0 ).z  <  -projCoords.z ){
           shadow-=0.2;
        }
    } 
    if(projCoords.z > 1.0)
        shadow = 0.0; 
    return shadow;
}

对不起所有代码,但我不知道问题出在哪里,它花了一周的时间进行搜索和调试,但没有任何进展.

sorry for all that code but I don't know where is the problem, it takes a week searching and debugging with no progress.

编辑 1-灯光位置向量为(-3.5f,8.0f,1.0f) 2-我将directShadowCalculation()更改为:

Edit 1- the light position vector is (-3.5f, 8.0f, 1.0f) 2- I changed the directShadowCalculation() to:

    float directShadowCalculation(){
    vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
    projCoords = projCoords * 0.5 + 0.5;
    float shadow = 1.0; 
    for (int i=0;i<4;i++){
        if ( texture( shadowMap, projCoords.xy + poissonDisk[i]/700.0 ).z  <  projCoords.z ){
           shadow-=0.2;
        }
    } 
    if(projCoords.z > 1.0)
        shadow = 0.0; 
    return shadow;
}

这是结果

推荐答案

如果必须将视图空间坐标转换为光源的本地空间,则必须在执行操作时设置lightSpaceMatrix:

If you have to convert view space coordinates to the local space of the light source, then the lightSpaceMatrix would have to be setup as you do it:

lightSpaceMatrix = lightSpaceProjection * (lightSpaceView*camInv)

因为,必须通过camInv从视图空间转换为世界空间.然后,您必须转换从光源(lightSpaceView)看的世界空间坐标.最后,您必须投影它lightSpaceProjection.

Because, you have to convert from view space to world space, by camInv. Then you have to convert the world space coordinates, as seen from the light source (lightSpaceView). And finaly you have to project it lightSpaceProjection.

但是您可以在顶点着色器中将世界坐标直接转换为光源的局部空间:

But you convert directly from world coordinates to the local space of the light source, in the vertex shader:

FragPosLightSpace = lightSpaceMatrix * vec4(FragPos,1.0); 

因此,您必须像这样设置lightSpaceMatrix:

Because of that you have to set up the lightSpaceMatrix like this:

lightSpaceMatrix = lightSpaceProjection * lightSpaceView


投影矩阵描述了从场景的3D点到视口的2D点的映射.它从视图(眼睛)空间转换到剪辑空间,然后通过除以剪辑坐标的 w 分量,将剪辑空间中的坐标转换为归一化设备坐标(NDC). NDC的范围是(-1,-1,-1)至(1,1,1). 这与正交投影或透视投影无关.


The projection matrix describes the mapping from 3D points of a scene, to 2D points of the viewport. It transforms from view (eye) space to the clip space, and the coordinates in the clip space are transformed to the normalized device coordinates (NDC) by dividing with the w component of the clip coordinates. The NDC are in range (-1,-1,-1) to (1,1,1). This is regardless of orthographic or perspective projection.

将投影片段位置(光空间)除以其 w 分量后,projCoords的范围是(-1,-1,-1)到(1,1, 1). projCoords = projCoords * 0.5 + 0.5;将XY坐标转换为纹理坐标,并将Z坐标转换为深度值[0,1].

After dividing the projected fragment position (light space) by its w component, the projCoords are in the range from (-1, -1, -1) to (1, 1, 1). projCoords = projCoords * 0.5 + 0.5; transforms the XY coordinates to texture coordinates, and transforms the Z coordinate to a depth value in the range [0, 1].

vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;

阴影测试中的反转没有意义,因为纹理的内容也应该是[0,1]范围内的深度值.
阴影测试应该看起来像这样:

The inversions in the shadow test make no sense, since the content of the texture should be also a depth value in the range [0,1].
The shadow test should look somehow like this:

if ( texture( shadowMap, projCoords.xy ).z < projCoords.z )
{
    ....
}

如果sun->direction是到太阳的方向,则必须这样设置lightSpaceView:

If sun->direction is the direction up to the sun, then lightSpaceView has to be set up like this:

glm::mat4 lightSpaceView = glm::lookAt(sun->direction, glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));

但是,如果sun->direction是太阳的照射方向,则必须这样设置lightSpaceView:

But, If sun->direction is the direction in which the sun shines, then lightSpaceView has to be set up like this:

glm::mat4 lightSpaceView = glm::lookAt(-sun->direction, glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));

由于对光使用正投影,并且光空间矩阵的原点靠近世界空间的原点,因此光投影的近平面应该在光空间的后面.否则,在生成光深度图时,太阳附近的物体将被光投影的近平面裁剪.

Since you use orthographic projection for the light and the origin of the light space matrix is near the origin of the worldspace, the near plane of the light projection should be far in the back of the light space. Otherwise the objects which are near to the the sun would be clipped, by the near plane of the light projection, when generating the light depth map.

glm::mat4 lightSpaceProjection = glm::ortho(-40.0f, 40.0f, -40.0f, 40.0f, -100.0f, 100.0f);


由于光照计算是在视图空间中完成的,因此您必须将光照位置从世界空间转换为视图空间:


Since the light calculations are done in view space, you have to convert the light positions from the world space to the view space:

GLuint directLightPosLoc = glGetUniformLocation(phongShader.getProgramID(), (const GLchar*) ("directLightPos[" + ToString((*it)->index) + "]").c_str());
glm::vec3 dir = glm::mat3(camera->getViewMatrix()) * (*it)->direction;
glUniform3fv( directLightPosLoc, 1, &dir[0] );


GLuint pointLightPosLoc = glGetUniformLocation(phongShader.getProgramID(), (const GLchar*) ("pointLightPos[" + ToString((*it)->index) + "]").c_str());
glm::vec4 pos = camera->getViewMatrix( * glm::vec4((*it)->position.x, (*it)->position.y, (*it)->position.z, 1.0);
glUniform3fv( directLightPosLoc, 1, &pos[0] );


GLuint spotLightPosLoc = glGetUniformLocation(phongShader.getProgramID(), (const GLchar*) ("spotLightPos[" + ToString((*it)->index) + "]").c_str());
glm::vec4 pos = camera->getViewMatrix( * glm::vec4((*it)->position.x, (*it)->position.y, (*it)->position.z, 1.0);
glUniform3fv( spotLightPosLoc, 1, &pos[0] );

请参见演示算法的WebGL示例:

See the WebGL example which demonstrates the algorithm:

(function loadscene() {

var sliderScale = 100.0
var gl;
var progShadow;
var progDraw;
var shadowFB;
var bufTorus;
var bufGround;
var canvas;
var vp_size;
var fb_size;

function render(deltaMS){

var ambient = document.getElementById( "ambient" ).value / sliderScale;
var diffuse = document.getElementById( "diffuse" ).value / sliderScale;
var specular = document.getElementById( "specular" ).value / sliderScale;
var shininess = document.getElementById( "shininess" ).value;

canvas = document.getElementById( "scene-canvas" );

var lightPos = [-3.0, 0.0, 2.0];
var lightAnimationMat = RotateAxis( IdentityMat44(), CalcAng( deltaMS, 20.0 ), 2 );
lightPos = Transform( lightPos, lightAnimationMat );
var lightDir = [ -lightPos[0], -lightPos[1], -lightPos[2] ];
var light  = Camera.Create( lightPos, [0, 0, 0], [0, 0, 1], 110, [ 5.0, 5.0 ], -20.0, 20.0 );
var camera = Camera.Create( [0, 2.5, 2], [0, 0, 0], [0, 0, 1], 110, [vp_size[0], vp_size[1]], 0.5, 100.0 );

var lightPrjMat = Camera.Ortho( light );
var lightViewMat = Camera.LookAt( light );
var prjMat = Camera.Perspective( camera );
var viewMat = Camera.LookAt( camera );
var modelMat = IdentityMat44();
modelMat = RotateAxis( modelMat, CalcAng( deltaMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( deltaMS, 17.0 ), 1 );
groundModelMat = IdentityMat44();
var viewLightDir = TransformVec( lightDir, viewMat );
    
gl.viewport( 0, 0, fb_size[0], fb_size[1] );
gl.enable( gl.DEPTH_TEST );

shadowFB.Bind( true );
ShaderProgram.Use( progShadow );
ShaderProgram.SetUniformM44( progShadow, "u_projectionMat44", lightPrjMat );
ShaderProgram.SetUniformM44( progShadow, "u_viewMat44", lightViewMat );
ShaderProgram.SetUniformM44( progShadow, "u_modelMat44", modelMat );
ShaderProgram.SetUniformF2( progShadow, "u_depthRange", [light.near, light.far] );

VertexBuffer.Draw( bufTorus );

gl.viewport( 0, 0, vp_size[0], vp_size[1] );
shadowFB.Release( true );
shadowFB.BindTexture( 1 );
ShaderProgram.Use( progDraw );
ShaderProgram.SetUniformM44( progDraw, "u_projectionMat44", prjMat );
ShaderProgram.SetUniformM44( progDraw, "u_viewMat44", viewMat );
ShaderProgram.SetUniformM44( progDraw, "u_lightProjectionMat44", lightPrjMat );
ShaderProgram.SetUniformM44( progDraw, "u_lightViewMat44", lightViewMat );
ShaderProgram.SetUniformM44( progDraw, "u_modelMat44", modelMat );
ShaderProgram.SetUniformI1( progDraw, "u_depthSampler", 1 );
ShaderProgram.SetUniformF3( progDraw, "u_lightDir", viewLightDir )
ShaderProgram.SetUniformF1( progDraw, "u_ambient", ambient )
ShaderProgram.SetUniformF1( progDraw, "u_diffuse", diffuse )
ShaderProgram.SetUniformF1( progDraw, "u_specular", specular )
ShaderProgram.SetUniformF1( progDraw, "u_shininess", shininess )

VertexBuffer.Draw( bufTorus );
ShaderProgram.SetUniformM44( progDraw, "u_modelMat44", groundModelMat );
VertexBuffer.Draw( bufGround );

requestAnimationFrame(render);
}

function nearestPow2( aSize ){
  return Math.pow( 2, Math.round( Math.log( aSize ) / Math.log( 2 ) ) ); 
}

function resize() {
    //vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
    vp_size = [window.innerWidth, window.innerHeight]
    canvas.width = vp_size[0];
    canvas.height = vp_size[1];

    var size = Math.max(256, Math.max(vp_size[0], vp_size[1]));
    size = nearestPow2(size/2);
    fb_size = [size, size]
    shadowFB = FrameBuffer.Create( fb_size ); 
}

function initScene() {

document.getElementById( "ambient" ).value = 0.2 * sliderScale;
document.getElementById( "diffuse" ).value = 0.7 * sliderScale;
document.getElementById( "specular" ).value = 0.5 * sliderScale;
document.getElementById( "shininess" ).value = 8.0;

canvas = document.getElementById( "scene-canvas");
vp_size = [canvas.width, canvas.height];
gl = canvas.getContext( "experimental-webgl" );
if ( !gl )
  return;

progShadow = ShaderProgram.Create( 
[ { source : "shadow-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "shadow-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if (!progShadow.progObj)
    return null;
progShadow.inPos = ShaderProgram.AttributeIndex( progShadow, "inPos" );
progShadow.inNV  = ShaderProgram.AttributeIndex( progShadow, "inNV" );
progShadow.inCol = ShaderProgram.AttributeIndex( progShadow, "inCol" );

progDraw = ShaderProgram.Create( 
  [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
    { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
  ] );
if (!progDraw.progObj)
    return null;
progDraw.inPos = ShaderProgram.AttributeIndex( progDraw, "inPos" );
progDraw.inNV  = ShaderProgram.AttributeIndex( progDraw, "inNV" );
progDraw.inCol = ShaderProgram.AttributeIndex( progDraw, "inCol" );

// create torus
var circum_size = 32, tube_size = 32;
var rad_circum = 1.0;
var rad_tube = 0.5;
var torus_pts = [];
var torus_nv = [];
var torus_col = [];
var torus_inx = [];
var col = [1, 0.5, 0.0];
for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
    var center = [
        Math.cos(2 * Math.PI * i_c / circum_size),
        Math.sin(2 * Math.PI * i_c / circum_size) ]
    for ( var i_t = 0; i_t < tube_size; ++ i_t ) {
        var tubeX = Math.cos(2 * Math.PI * i_t / tube_size)
        var tubeY = Math.sin(2 * Math.PI * i_t / tube_size)
        var pt = [
            center[0] * ( rad_circum + tubeX * rad_tube ),
            center[1] * ( rad_circum + tubeX * rad_tube ),
            tubeY * rad_tube ]
        var nv = [ pt[0] - center[0] * rad_tube, pt[1] - center[1] * rad_tube, tubeY * rad_tube ]
        torus_pts.push( pt[0], pt[1], pt[2] );
        torus_nv.push( nv[0], nv[1], nv[2] );
        torus_col.push( col[0], col[1], col[2] );
        var i_cn = (i_c+1) % circum_size
        var i_tn = (i_t+1) % tube_size
        var i_c0 = i_c * tube_size; 
        var i_c1 = i_cn * tube_size; 
        torus_inx.push( i_c0+i_t, i_c0+i_tn, i_c1+i_t, i_c0+i_tn, i_c1+i_t, i_c1+i_tn )
    }
}
bufTorus = VertexBuffer.Create(
  [ { data : torus_pts, attrSize : 3, attrLoc : progDraw.inPos },
    { data : torus_nv,  attrSize : 3, attrLoc : progDraw.inNV },
    { data : torus_col, attrSize : 3, attrLoc : progDraw.inCol } ],
    torus_inx
);

var g_l = 8.0;
var g_h = -2.5;
var g_c = [ 0.8, 0.6, 0.8 ];
bufGround = VertexBuffer.Create( 
    [ { data : [ -g_l, -g_l, g_h, g_l, -g_l, g_h, g_l, g_l, g_h, -g_l, g_l, g_h ], attrSize : 3, attrLoc : progDraw.inPos },
      { data : [ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0 ], attrSize : 3, attrLoc : progDraw.inNV },
      { data : [ g_c[0], g_c[1], g_c[2], g_c[0], g_c[1], g_c[2], g_c[0], g_c[1], g_c[2], g_c[0], g_c[1], g_c[2] ], attrSize : 3, attrLoc : progDraw.inCol } ],
    [ 0, 1, 2, 0, 2, 3 ]
);

window.onresize = resize;
resize();
requestAnimationFrame(render);
}

var startTime;
function Fract( val ) { 
    return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
    return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}

function IdentityMat44() { return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; }

function RotateAxis(matA, angRad, axis) {
    var aMap = [ [1, 2], [2, 0], [0, 1] ];
    var a0 = aMap[axis][0], a1 = aMap[axis][1]; 
    var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
    var matB = IdentityMat44();
    for ( var i = 0; i < 16; ++ i ) matB[i] = matA[i];
    for ( var i = 0; i < 3; ++ i ) {
        matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng;
        matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng;
    }
    return matB;
}

function Cross( a, b ) { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0 ]; }
function Dot( a, b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
function Normalize( v ) {
    var len = Math.sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
    return [ v[0] / len, v[1] / len, v[2] / len ];
}

Transform = function(vec, mat) {
    return [
        vec[0] * mat[0*4+0] + vec[1] * mat[1*4+0] + vec[2] * mat[2*4+0] + mat[3*4+0],
        vec[0] * mat[0*4+1] + vec[1] * mat[1*4+1] + vec[2] * mat[2*4+1] + mat[3*4+1],
        vec[0] * mat[0*4+2] + vec[1] * mat[1*4+2] + vec[2] * mat[2*4+2] + mat[3*4+2],
        vec[0] * mat[0*4+3] + vec[1] * mat[1*4+3] + vec[2] * mat[2*4+3] + mat[3*4+3] ]
    if ( h[3] == 0.0 )
        return [0, 0, 0]
    return [ h[0]/h[3], h[1]/h[3], h[2]/h[3] ];
}

TransformVec = function(vec, mat) {
   return [
        vec[0] * mat[0*4+0] + vec[1] * mat[1*4+0] + vec[2] * mat[2*4+0],
        vec[0] * mat[0*4+1] + vec[1] * mat[1*4+1] + vec[2] * mat[2*4+1],
        vec[0] * mat[0*4+2] + vec[1] * mat[1*4+2] + vec[2] * mat[2*4+2] ]
}

var Camera = {};
Camera.Create = function( pos, target, up, fov_y, vp, near, far ) {
    var camera = {};
    camera.pos    = pos;
    camera.target = target;
    camera.up     = up;
    camera.fov_y  = fov_y;
    camera.vp     = vp;
    camera.near   = near;
    camera.far    = far;
    return camera;
}
Camera.Ortho = function( camera ) {
    var fn = camera.far + camera.near;
    var f_n = camera.far - camera.near;
    var w = camera.vp[0];
    var h = camera.vp[1];
    var m = IdentityMat44();
    m[0]  = 2 / w; m[1]  = 0;     m[2]  =  0;        m[3]  = 0;
    m[4]  = 0;     m[5]  = 2 / h; m[6]  =  0;        m[7]  = 0;
    m[8]  = 0;     m[9]  = 0;     m[10] = -2 / f_n;  m[11] = 0;
    m[12] = 0;     m[13] = 0;     m[14] = -fn / f_n; m[15] = 1;
    return m;
}
Camera.Perspective = function( camera ) {
    var fn = camera.far + camera.near;
    var f_n = camera.far - camera.near;
    var r = camera.vp[0] / camera.vp[1];
    var t = 1 / Math.tan( Math.PI * camera.fov_y / 360 );
    var m = IdentityMat44();
    m[0]  = t/r; m[1]  = 0; m[2]  =  0;                              m[3]  = 0;
    m[4]  = 0;   m[5]  = t; m[6]  =  0;                              m[7]  = 0;
    m[8]  = 0;   m[9]  = 0; m[10] = -fn / f_n;                       m[11] = -1;
    m[12] = 0;   m[13] = 0; m[14] = -2 * camera.far * camera.near / f_n; m[15] =  0;
    return m;
}
Camera.LookAt = function( camera ) {
    var mz = Normalize( [ camera.pos[0]-camera.target[0], camera.pos[1]-camera.target[1], camera.pos[2]-camera.target[2] ] );
    var mx = Normalize( Cross( camera.up, mz ) );
    var my = Normalize( Cross( mz, mx ) );
    var tx = Dot( mx, camera.pos );
    var ty = Dot( my, camera.pos );
    var tz = Dot( [-mz[0], -mz[1], -mz[2]], camera.pos ); 
    var m = IdentityMat44();
    m[0]  = mx[0]; m[1]  = my[0]; m[2]  = mz[0]; m[3]  = 0;
    m[4]  = mx[1]; m[5]  = my[1]; m[6]  = mz[1]; m[7]  = 0;
    m[8]  = mx[2]; m[9]  = my[2]; m[10] = mz[2]; m[11] = 0;
    m[12] = tx;    m[13] = ty;    m[14] = tz;    m[15] = 1; 
    return m;
} 

var ShaderProgram = {};
ShaderProgram.Create = function (shaderList) {
    var shaderObjs = [];
    for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
        var shderObj = this.CompileShader(shaderList[i_sh].source, shaderList[i_sh].stage);
        if (shderObj == 0)
            return 0;
        shaderObjs.push(shderObj);
    }
    var prog = {}
    prog.progObj = this.LinkProgram(shaderObjs)
    if (prog.progObj) {
        prog.attribIndex = {};
        var noOfAttributes = gl.getProgramParameter(prog.progObj, gl.ACTIVE_ATTRIBUTES);
        for (var i_n = 0; i_n < noOfAttributes; ++i_n) {
            var name = gl.getActiveAttrib(prog.progObj, i_n).name;
            prog.attribIndex[name] = gl.getAttribLocation(prog.progObj, name);
        }
        prog.unifomLocation = {};
        var noOfUniforms = gl.getProgramParameter(prog.progObj, gl.ACTIVE_UNIFORMS);
        for (var i_n = 0; i_n < noOfUniforms; ++i_n) {
            var name = gl.getActiveUniform(prog.progObj, i_n).name;
            prog.unifomLocation[name] = gl.getUniformLocation(prog.progObj, name);
        }
    }
    return prog;
}
ShaderProgram.AttributeIndex = function (prog, name) { return prog.attribIndex[name]; }
ShaderProgram.UniformLocation = function (prog, name) { return prog.unifomLocation[name]; }
ShaderProgram.Use = function (prog) { gl.useProgram(prog.progObj); }
ShaderProgram.SetUniformI1 = function (prog, name, val) { if (prog.unifomLocation[name]) gl.uniform1i(prog.unifomLocation[name], val); }
ShaderProgram.SetUniformF1 = function (prog, name, val) { if (prog.unifomLocation[name]) gl.uniform1f(prog.unifomLocation[name], val); }
ShaderProgram.SetUniformF2 = function (prog, name, arr) { if (prog.unifomLocation[name]) gl.uniform2fv(prog.unifomLocation[name], arr); }
ShaderProgram.SetUniformF3 = function (prog, name, arr) { if (prog.unifomLocation[name]) gl.uniform3fv(prog.unifomLocation[name], arr); }
ShaderProgram.SetUniformF4 = function (prog, name, arr) { if (prog.unifomLocation[name]) gl.uniform4fv(prog.unifomLocation[name], arr); }
ShaderProgram.SetUniformM33 = function (prog, name, mat) { if (prog.unifomLocation[name]) gl.uniformMatrix3fv(prog.unifomLocation[name], false, mat); }
ShaderProgram.SetUniformM44 = function (prog, name, mat) { if (prog.unifomLocation[name]) gl.uniformMatrix4fv(prog.unifomLocation[name], false, mat); }
ShaderProgram.CompileShader = function (source, shaderStage) {
    var shaderScript = document.getElementById(source);
    if (shaderScript)
        source = shaderScript.text;
    var shaderObj = gl.createShader(shaderStage);
    gl.shaderSource(shaderObj, source);
    gl.compileShader(shaderObj);
    var status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
    if (!status) alert(gl.getShaderInfoLog(shaderObj));
    return status ? shaderObj : null;
}
ShaderProgram.LinkProgram = function (shaderObjs) {
    var prog = gl.createProgram();
    for (var i_sh = 0; i_sh < shaderObjs.length; ++i_sh)
        gl.attachShader(prog, shaderObjs[i_sh]);
    gl.linkProgram(prog);
    status = gl.getProgramParameter(prog, gl.LINK_STATUS);
    if (!status) alert("Could not initialise shaders");
    gl.useProgram(null);
    return status ? prog : null;
}

var VertexBuffer = {};
VertexBuffer.Create = function( attributes, indices ) {
    var buffer = {};
    buffer.buf = [];
    buffer.attr = []
    for ( var i = 0; i < attributes.length; ++ i ) {
        buffer.buf.push( gl.createBuffer() );
        buffer.attr.push( { size : attributes[i].attrSize, loc : attributes[i].attrLoc } );
        gl.bindBuffer( gl.ARRAY_BUFFER, buffer.buf[i] );
        gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( attributes[i].data ), gl.STATIC_DRAW );
    }
    buffer.inx = gl.createBuffer();
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, buffer.inx );
    gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW );
    buffer.inxLen = indices.length;
    gl.bindBuffer( gl.ARRAY_BUFFER, null );
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
    return buffer;
}
VertexBuffer.Draw = function( bufObj ) {
  for ( var i = 0; i < bufObj.buf.length; ++ i ) {
        gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.buf[i] );
        gl.vertexAttribPointer( bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0 );
        gl.enableVertexAttribArray( bufObj.attr[i].loc );
    }
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
    gl.drawElements( gl.TRIANGLES, bufObj.inxLen, gl.UNSIGNED_SHORT, 0 );
    for ( var i = 0; i < bufObj.buf.length; ++ i )
       gl.disableVertexAttribArray( bufObj.attr[i].loc );
    gl.bindBuffer( gl.ARRAY_BUFFER, null );
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}

var FrameBuffer = {};
FrameBuffer.Create = function( vp, texturePlan ) {
    var texPlan = texturePlan ? new Uint8Array( texturePlan ) : null;
    var fb = gl.createFramebuffer();
    var fbsize = Math.max(vp[0], vp[1]);
    fbsize = 1 << 31 - Math.clz32(fbsize); // nearest power of 2
    fb.width = fbsize;
    fb.height = fbsize;
    gl.bindFramebuffer( gl.FRAMEBUFFER, fb );
    fb.color0_texture = gl.createTexture();
    gl.bindTexture( gl.TEXTURE_2D, fb.color0_texture );
    gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, fb.width, fb.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, texPlan );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
    gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
    fb.renderbuffer = gl.createRenderbuffer();
    gl.bindRenderbuffer( gl.RENDERBUFFER, fb.renderbuffer );
    gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, fb.width, fb.height );
    gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fb.color0_texture, 0 );
    gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, fb.renderbuffer );
    gl.bindTexture( gl.TEXTURE_2D, null );
    gl.bindRenderbuffer( gl.RENDERBUFFER, null );
    gl.bindFramebuffer( gl.FRAMEBUFFER, null );

    fb.Bind = function( clear ) {
        gl.bindFramebuffer( gl.FRAMEBUFFER, this );
        if ( clear ) {
            gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
            gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
        }
    };

    fb.Release = function( clear ) {
        gl.bindFramebuffer( gl.FRAMEBUFFER, null );
        if ( clear ) {
            gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
            gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
        }
    };

    fb.BindTexture = function( textureUnit ) {
        gl.activeTexture( gl.TEXTURE0 + textureUnit );
        gl.bindTexture( gl.TEXTURE_2D, this.color0_texture );
    };

    return fb;
}

initScene();
  
})();

html,body {
    height: 100%;
    width: 100%;
    margin: 0;
    overflow: hidden;
}

#gui {
    position : absolute;
    top : 0;
    left : 0;
}

<script id="shadow-shader-vs" type="x-shader/x-vertex">
  precision mediump float;
  
  attribute vec3 inPos;
  attribute vec3 inNV;
  attribute vec3 inCol;
  
  varying vec3 vertPos;
  varying vec3 vertNV;
  varying vec3 vertCol;
  varying vec4 vPosPrj;
  
  uniform mat4 u_projectionMat44;
  uniform mat4 u_viewMat44;
  uniform mat4 u_modelMat44;
  
  void main()
  {
      vec3 modelNV  = mat3( u_modelMat44 ) * normalize( inNV );
      vertNV        = mat3( u_viewMat44 ) * modelNV;
      vertCol       = inCol;
      vec4 modelPos = u_modelMat44 * vec4( inPos, 1.0 );
      vec4 viewPos  = u_viewMat44 * modelPos;
      vertPos       = viewPos.xyz / viewPos.w;
      vPosPrj       = u_projectionMat44 * viewPos;
      gl_Position   = vPosPrj;
  }
</script>

<script id="shadow-shader-fs" type="x-shader/x-fragment">
    precision mediump float;
    
    varying vec3 vertPos;
    varying vec3 vertNV;
    varying vec3 vertCol;
    varying vec4 vPosPrj;

    uniform vec2 u_depthRange;

    vec3 PackDepth( in float depth )
    {
        float depthVal = depth * (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
        vec4 encode = fract( depthVal * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
        return encode.xyz - encode.yzw / 256.0 + 1.0/512.0;
    }
    
    void main()
    {
        float ndc_depth = vPosPrj.z / vPosPrj.w;
        float nearZ     = u_depthRange.x;
        float farZ      = u_depthRange.y;
        float depth     = ndc_depth * 0.5 + 0.5;
        gl_FragColor    = vec4( PackDepth( depth ).xyz, 1.0 );
    }
</script>

<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;

attribute vec3 inPos;
attribute vec3 inNV;
attribute vec3 inCol;

varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
varying vec4 lightPrj;
varying vec4 vPosPrj;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
uniform mat4 u_lightProjectionMat44;
uniform mat4 u_lightViewMat44;

void main()
{
    vec3 modelNV  = mat3( u_modelMat44 ) * normalize( inNV );
    vertNV        = mat3( u_viewMat44 ) * modelNV;
    vertCol       = inCol;
    vec4 modelPos = u_modelMat44 * vec4( inPos, 1.0 );
    vec4 lightPos = u_lightViewMat44 * modelPos;
    vec4 viewPos  = u_viewMat44 * modelPos;
    lightPrj      = u_lightProjectionMat44 * lightPos;
    vertPos       = viewPos.xyz / viewPos.w;
    vPosPrj       = u_projectionMat44 * viewPos;
    gl_Position   = vPosPrj;
}
</script>
  
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;

varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
varying vec4 lightPrj;
varying vec4 vPosPrj;

uniform sampler2D u_depthSampler;
uniform vec3      u_lightDir;
uniform float     u_ambient;
uniform float     u_diffuse;
uniform float     u_specular;
uniform float     u_shininess;

float UnpackDepth( in vec3 pack )
{
  float depth = dot( pack, 1.0 / vec3(1.0, 256.0, 256.0*256.0) );
  return depth * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
}

float Depth( in sampler2D depthSampler, in vec2 texC )
{
  vec3 depthVal = texture2D( depthSampler, texC.st ).xyz;  
  return UnpackDepth( depthVal.rgb );
}

void main()
{
    vec3  ndc_light  = lightPrj.xyz / lightPrj.w;
    vec2  lightTexC  = ndc_light.xy * 0.5 + 0.5;
    float lightDepth = ndc_light.z * 0.5 + 0.5;
    float testDepth  = Depth( u_depthSampler, lightTexC );
    float shadow     = step( lightDepth-0.01, testDepth ) + step( testDepth, 0.0 );
    vec3  color      = vertCol;
    vec3  lightCol   = u_ambient * color;
    vec3  normalV    = normalize( vertNV );
    vec3  lightV     = normalize( -u_lightDir );
    float NdotL      = max( 0.0, dot( normalV, lightV ) );
    lightCol        += shadow * NdotL * u_diffuse * color;
    vec3  eyeV       = normalize( -vertPos );
    vec3  halfV      = normalize( eyeV + lightV );
    float NdotH      = max( 0.0, dot( normalV, halfV ) );
    float kSpecular  = ( u_shininess + 2.0 ) * pow( NdotH, u_shininess ) / ( 2.0 * 3.14159265 );
    lightCol        += shadow * kSpecular * u_specular * color;
    gl_FragColor     = vec4( lightCol.rgb, 1.0 );
}
</script>

<div><form id="gui" name="inputs"><table>
    <tr> <td> <font color= #CCF>ambient</font> </td> 
            <td> <input type="range" id="ambient" min="0" max="100" value="0"/></td> </tr>
    <tr> <td> <font color= #CCF>diffuse</font> </td> 
            <td> <input type="range" id="diffuse" min="0" max="100" value="0"/></td> </tr>
    <tr> <td> <font color= #CCF>specular</font> </td> 
            <td> <input type="range" id="specular" min="0" max="100" value="0"/></td> </tr>
    <tr> <td> <font color= #CCF>shininess</font> </td> 
            <td> <input type="range" id="shininess" min="0" max="100" value="0"/></td> </tr>
</table></form></div>

<canvas id="scene-canvas" style="border: none;" width="512" height="512"></canvas>


另请参见


See also

  • How to render depth linearly in modern OpenGL with gl_FragCoord.z in fragment shader
  • Transform the modelMatrix

这篇关于OpenGL ES3阴影贴图问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆