WebGL实践篇(十)—— 光照:聚光灯

1.聚光灯的实现

 

光线从某个点照亮所有方向,而聚光灯只选择其中一个方向,将其他光线方向与所选方向点乘并选择限定范围,判断光线是否在限定范围内,如果不在则不照亮。

 首先给定一个点乘限定,如果与选定聚光灯方向的点乘大于这个点乘限定,则照亮,否则不照亮。

片段着色器:

  <script id="fragment-shader-3D" type="x-shader/x-fragment">
    precision mediump float;

    varying vec3 v_normal;
    varying vec3 v_surfaceToLight;
    varying vec3 v_surfaceToView;
    
    uniform vec4 u_color;
    uniform float u_shininess;

    uniform vec3 u_lightDirection;
    uniform float u_limit;
    
    void main() {
      vec3 normal = normalize(v_normal);

      vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
      vec3 surfaceToViewDirection = normalize(v_surfaceToView);
      vec3 halfVector = normalize(surfaceToLightDirection + surfaceToViewDirection);

      float light = 0.0;
      float specular = 0.0;

      float dotFromDirection = dot(surfaceToLightDirection,-u_lightDirection);

      if(dotFromDirection >= u_limit){
        light = dot(normal,surfaceToLightDirection);
        if(light>0.0){
          specular = pow(dot(normal,halfVector),u_shininess);
        }
      }

    
      gl_FragColor = u_color;
    
      gl_FragColor.rgb *= light;

      gl_FragColor.rgb += specular;
    } 
   </script>

对u_lightDirection取负是因为我们希望两个方向匹配的时候能指向相同的方向,也可以传入时就直接传反方向(u_reverseLightDirection)

传参:

    var lightdUniformLocation = webgl.getUniformLocation(program, "u_lightDirection");
    var limitUniformLocation = webgl.getUniformLocation(program, "u_limit");

    //注意这段代码
    var lightPosition = [40, 60, 120];
    var lmat = m4.lookAt(lightPosition, targetPosition, up);
    lmat = m4.multiply(m4.xRotation(lightRotationX), lmat);
    lmat = m4.multiply(m4.yRotation(lightRotationY), lmat);
    lightDirection = [-lmat[8], -lmat[9], -lmat[10]]

    webgl.uniform3fv(lightdUniformLocation, lightDirection);
    webgl.uniform1f(limitUniformLocation, Math.cos(limit));

注意到我们的ligtDirection运用到了矩阵的计算,可以联想到之前说过的,如果需要实现视线跟随的效果,那么就需要利用到lookAt()方法。

结果如下:

 左右上下移动可改变聚光灯的方向。

片段着色器的优化:

利用step()函数优化if语句,着色器中最好避免出现if语句

      float inlight = step(u_limit,dotFromDirection);
      float light = inlight * dot(normal,surfaceToLightDirection);
      float specular = inlight * pow(dot(normal,halfVector),u_shininess);

2.聚光灯的优化

将聚光灯的光做一个过渡。用一个内部限定和一个外部限定代替原来的一个限定值,如果在内部限定内就使用1,在外部限定外就用0,在二者之间就使用0-1之间的插值。

  <script id="fragment-shader-3D" type="x-shader/x-fragment">
    precision mediump float;

    varying vec3 v_normal;
    varying vec3 v_surfaceToLight;
    varying vec3 v_surfaceToView;
    
    uniform vec4 u_color;
    uniform float u_shininess;

    uniform vec3 u_lightDirection;
    uniform float u_innerLimit;
    uniform float u_outerLimit;
    
    void main() {
      vec3 normal = normalize(v_normal);

      vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
      vec3 surfaceToViewDirection = normalize(v_surfaceToView);
      vec3 halfVector = normalize(surfaceToLightDirection + surfaceToViewDirection);

      float dotFromDirection = dot(surfaceToLightDirection,-u_lightDirection);
      float limitRange = u_innerLimit - u_outerLimit;
      //插值
      float inlight = clamp((dotFromDirection - u_outerLimit)/limitRange,0.0,1.0);
      float light = inlight * dot(normal,surfaceToLightDirection);
      float specular = inlight * pow(dot(normal,halfVector),u_shininess);

    
      gl_FragColor = u_color;
    
      gl_FragColor.rgb *= light;

      gl_FragColor.rgb += specular;
    } 
   </script>

注意:必须要保证传到u_innerLimit和u_outerLimit不能相等,否则着色器当中会出现undefined。

定义参数:

    var inlimitUniformLocation = webgl.getUniformLocation(program, "u_innerLimit");
    var outlimitUniformLocation = webgl.getUniformLocation(program, "u_outerLimit");

    webgl.uniform1f(inlimitUniformLocation, Math.cos(innerLimit));
    webgl.uniform1f(outlimitUniformLocation, Math.cos(outerLimit));

优化着色器代码(smoothstep代码相当于替代插值函数):

      float inlight = smoothstep(u_outerLimit,u_innerLimit,dotFromDirection);

结果如下:

 smoothstep使用的时Hermite插值:

当lowBound大于或等于upperBound时,smoothstep方法会产生undefined,对于正常的聚光灯,这种情况永远不会出现。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值