• BlinnPhong反射模型实践(web实现)


    Blinn-Phong反射模型实践(web实现)

    games101 第四次作业

    最终完成带贴图的 Blinn-Phong 模型,产生光照效果

    展示图

    完成了

    1. 不带贴图的 Blinn-Phone 反射模型
    2. 带贴图的模型,但是纹理映射应用在顶点着色器上
    3. 带贴图的模型,纹理映射在片元着色器上

    Blinn-Phone 光照模型

    光照分为三种,分别为环境光,漫反射光和类镜面反射的高光。这分别对应三种反射,当光照射在物体表面,物体表面会发生相应的反射,将光反射到人眼当中,这样,人眼才能看见物体。

    环境光简单理解为任何地方都有的一种光,光的颜色和强度相同,当然,现实可不是这样;漫反射在初中学过,在物体表面某个点发生漫反射后,从任何地方都能看到这个点且颜色强度都一样;高光反射类似光照到镜子上产生的高亮的效果。设置进入人眼的光为 L,漫反射为 Ld,高光反射为 Ls,环境反射为 La,可以有公式

    \[\begin{equation} L = L_a+L_d+Ls \end{equation} \]

    环境反射

    环境光由于任何地方的光照强度相同,因此有公式

    \[L_a = k_a \times I_a \]

    ka 代表了当前影响因子,可以令其为颜色,Ia 是光照强度,所有光照强度一致

    环境光

    该图是仅仅使用环境光和贴图产生的效果,Ia 设置为了 0.39,ka 设置为 vec3(1.0, 1.0, 1.0)。

    漫反射

    初中物理可以了解到,漫反射光强度一样,这里定义的漫反射光,从任何地方看向反射的点,强度都一样

    由下图可以看到,I 表示原始光照强度,在三维空间中,光照强度以球面的形式向外扩散,因此强度衰减与球面积有关,假如光照与物体表面的点距离为 r,则在点处的光强为I/r^2 ;光从正面照向一个物体和从侧面照向一个物体强度是不同的,从正面照强度最大,可以得到当前点应角度产生的影响为n*l;如果 n*l为负值,说明光在背面,无法被看到,因此有了下述图片展示的公式

    漫反射

    Ld 表示漫反射强度,kd 表示影响因子,可以为颜色,I 为初始光照强度,r 为光源距离物体点的距离,n 为当前点的法向量,l 为点到光源的方向向量。将环境反射和漫反射加入程序中可得

    漫反射1

    单独和环境反射进行对比可以发现,牛牛变得更有轮廓了

    高光反射

    高光反射最为复杂,最重要的一点,当反射角与入射角大小一致时,光照强度最大。首先依下图所示定义多个变量,I 为点到光的方向向量,n 为点的法向量,v 为点到视角的方向向量。现在,我们知道,反射角与入射角一致时,强度最大,若此时反射角设为入射角大小,那么 v 与反射线的夹角越大,损失的光照越多,而 v 与反射线夹角可以转换为 n 与 h 的夹角。

    h 叫半城向量,实际上就是 I 向量与 v 向量的角平分线的方向向量。此时因为 n 和 h 的夹角产生的光照损失值为 n * h,其他部分与漫反射一致。I / r^2表示光照随距离的缩减,ks 表示影响因子

    高光反射

    将上述三种反射集合起来形成了 Blinn-Phone 反射模型,和前面两种对比,有了明显的高光

    高光反射2

    顶点着色器和片元着色器

    在顶点着色器中,会遍历每个顶点的属性进行处理,此时的运算均是三角顶点的运算。WebGL 提供了 Shader 的加载,从顶点着色器中可以定义 vary 变量类型,并在片元着色器中接收

    • 如果传输的是顶点坐标,那么在片元着色器中接收的是三角形内部像素点的顶点坐标
    • 如果传输的是法向量,那么在片元着色器中接收到的是三角形内部经过插值后的法向量
    • 如果传输的是顶点颜色,那么在片元着色器中接收到的是颜色插值
    • 其他均如此,在片元着色器中会经过插值计算后产生逐像素的插值

    逐顶点计算的反射代码

    // 实现了自定义的颜色的 blinn-phong reflection
    const vertexShader = `
    // vec3 normal, uv,
    varying vec3 vColor;  
    void main(){
        // Ld 漫反射
        vec3 lightPoint = vec3(3.0, 4.0, -3.0);
        vec3 l = normalize(lightPoint - position);
        float radius1 = distance(lightPoint, position);
        // Kd
        vec3 LdColor = vec3(1.0, 0.5, 0.7);
        float Id = 1.0;
        vec3 Ld = LdColor*(Id/radius1)*max(0.0, dot(normal,l));
        
        // La 环境光
        vec3 LaColor = vec3(1.0, 1.0, 1.0);
        float Ia = 0.2;
        // vec3 La = LaColor*Ia;
        // 用自己的颜色
        vec3 La = LdColor*Ia;
        
        // Ls 镜面反射光
        vec3 LsColor = vec3(1.0, 1.0, 1.0);
        float Is =1.0;
        vec3 vs = normalize(cameraPosition-position);
        vec3 hs = normalize(vs+l);
        float p = 30.0;
        vec3 Ls = LsColor*(Is/radius1)* pow(max(0.0, dot(normal, hs)), p);
        
        vColor = La + Ld + Ls;
        
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
    `;
    
    const fragmentShader = `
    varying vec3 vColor; 
    void main(){
        gl_FragColor = vec4(vColor, 1.0);
    }
    `;
    
    

    逐片元计算的反射代码

    //法线贴图
    const vertexShader3 = `
    // vec3 normal, uv,
    varying vec3 vPosition;
    varying vec3 vNormal;
    varying vec2 vUv;
    void main(){
        vPosition = position;
        vUv = uv;
        vNormal = normal;
        
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
    `;
    
    //法线贴图处理
    const fragmentShader3 = `
    uniform sampler2D cowTexture;
    uniform sampler2D cowNormalTexture;
    varying vec3 vPosition;
    varying vec3 vNormal;
    varying vec2 vUv;
    void main(){
    
        vec4 map1 = texture2D(cowTexture, vUv); 
        vec3 selfColor = vec3(map1.x, map1.y, map1.z);
        
        // 法线贴图将改变 normal
        float uvAfterU = (vUv.x*1024.0+1.0)/1024.0;
        float uvAfterV = (vUv.y*1024.0+1.0)/1024.0;
        float c1 = 1.0;
        float c2 = 1.0;
        vec4 currentDepth = texture2D(cowNormalTexture, vUv);
        vec4 currentDepthU = texture2D(cowNormalTexture, vec2(uvAfterU, vUv.y));
        vec4 currentDepthV = texture2D(cowNormalTexture, vec2(vUv.x, uvAfterV));
        // float dp_u = c1 * 
        if(uvAfterU<=1.0 && uvAfterV<=1.0){
            
        }
    
        // Ld 漫反射
        vec3 lightPoint = vec3(3.0, 4.0, -3.0);
        vec3 l = normalize(lightPoint - vPosition);
        float radius1 = distance(lightPoint, vPosition);
        // Kd
        vec3 LdColor = vec3(1.0, 1.0, 1.0);
        float Id = 1.0;
        vec3 Ld = selfColor*(Id/radius1)*max(0.0, dot(vNormal,l));
        //
        // La 环境光
        vec3 LaColor = vec3(1.0, 1.0, 1.0);
        float Ia = 0.39;
        // vec3 La = LaColor*Ia;
        // 用自己的颜色
        vec3 La = selfColor*Ia;
        
        // Ls 镜面反射光
        vec3 LsColor = vec3(1.0, 1.0, 1.0);
        float Is =1.0;
        vec3 vs = normalize(cameraPosition-vPosition);
        vec3 hs = normalize(vs+l);
        float p = 30.0;
        vec3 Ls = selfColor*(Is/radius1)* pow(max(0.0, dot(vNormal, hs)), p);
        
        // 整体颜色
        vec3 vColor = La + Ld + Ls;
        // vec3 vColor = La + Ld ;
        // vec3 vColor = vec3(1.0, 0.6, 0.8);
    
        gl_FragColor = vec4(vColor, 1.0);
    }
    `;
    

    在之前的实践中采用了纯 CPU 的计算方式,原生 canvas API 渲染,但是效率和处理太慢,因此,后续的实验采用 threejs 框架加快实践效率

    希望读者在看完后能提出意见, 点个赞, 鼓励一下, 我们一起进步. 加油 !!
  • 相关阅读:
    [算法初步]希尔排序
    逆波兰表达式1(简介)
    [数据结构]之链表
    [数据结构]之栈
    [算法初步]之归并排序
    [算法初步]之快速排序
    [算法初步]之冒泡排序
    逆波兰表达式2中缀转后缀表达式)
    [算法初步]之简单选择排序
    [数据结构]之顺序表
  • 原文地址:https://www.cnblogs.com/xiaxiangx/p/15789992.html
Copyright © 2020-2023  润新知