urp草地渲染学习

URP草地渲染学习笔记


这是一篇关于colin 大佬草地的 学习笔记 link

  • 准备工作

    1. 按照草地所在的位置 划分不同的cell , 每个cell 记录 cell里面草的位置
    2. 根据cell 列表 有序的生成一个草的位置列表,并将这个位置列表保存到ComputeBuffer
      	 // 将所有排序完之后的草放入到cell 列表中 传入shader
          Vector3[] allGrassPosSortByCell = new Vector3[m_grassCnt];
          int offset = 0;
          for (int i = 0; i < m_cellToPosList.Length; i++) {
              for (int j = 0; j < m_cellToPosList[i].Count; j++)
              {
                  allGrassPosSortByCell[offset] = m_cellToPosList[i][j];
                  offset ++;
              }
          }
      
      
      
  • 更新流程

    1. 将摄像机的可视距离调整到 草的可视距离范围

    2. 取得摄像机的视锥体, 将每个cell 转换成AABB包围盒,计算包围盒是否在视锥体内,粗略的剔除不在视野内的cell

    3. 以每个cell为单位,计算当前cell 处于 位置列表的偏移值,将偏移值传递到ComputeShader

       for (var i = 0; i < m_visibleCellIdList.Count; i++)
          {
              var cellId = m_visibleCellIdList[i];
              // 计算偏移起点
              int memoryOffset = 0;
              for (var j = 0; j < cellId; j++)
              {
                  memoryOffset += m_cellToPosList[j].Count;
              }
          
              cullingComputeShader.SetInt("_StartOffset", memoryOffset);
              var checkCnt = m_cellToPosList[cellId].Count;
              
              cullingComputeShader.Dispatch(0, Mathf.CeilToInt(checkCnt / 64f),1,1);
          }
      
    4. ComputeShader 计算 当前草是否在视野内,将在视野内的草的id添加到列表里

      [numthreads(64,1,1)]
      void CSMain(uint3 id : SV_DispatchThreadID){
          // 将世界坐标做换成裁剪坐标
          float4 absPosCS = abs(mul(_VPMatrix, float4(_GrassAllPosBuffer[id.x + _StartOffset], 1.0)));
          
          //_GrassVisibleIDBuffer.Append(id.x + _StartOffset);
          // 判断坐标在npc下的裁剪情况
          if(absPosCS.z /  <= absPosCS.w  && absPosCS.y <= absPosCS.w * 1.5 && absPosCS.x <= absPosCS.w * 1.1 && absPosCS.w <= _MaxDistance){
              _GrassVisibleIDBuffer.Append(id.x + _StartOffset);
          }
      
      }
      
    5. 获取ComputeShader计算好的信息,更新ArgsBuff里的绘制次数,使用DrawMeshInstancedIndirect 批量绘制 草地

        // 获取当前草地的个数
        ComputeBuffer.CopyCount(m_grassVisibleIDBuffer, m_grassRenderArgsBuffer, 4);
        // 绘制草地
        Graphics.DrawMeshInstancedIndirect(m_grassMesh, 0, instanceMaterial, m_renderBounds, m_grassRenderArgsBuffer);
      
      

裁剪后的效果
在这里插入图片描述

  • 渲染流程

    1. Instanced 的 shader中 会带有当前实例化的id, 由上面经过Cpu 跟ComputeShader计算 可以得出 当前可见草的id, 跟位置列表,综上就可以得出每个草所在的世界坐标,
        Varyings vert(Attributes input , uint instanceID : SV_InstanceId){
         float3  perGrassPivotPosWS = _GrassAllPosBuffer[_GrassVisibleIDBuffer[instanceID]].xyz ;}
      
    2. 因为我们用的草是只一个面 所以它需要时刻对着摄像机,也就是billboard.
    3. 直接在顶点着色器中计算当前的颜色, 根据 当前草的高度插值计算一下当前的颜色 可以形成一种从草根到草尖 一种由暗到明的效果,最后加上漫反射跟高光反射的效果
      渲染后的效果在这里插入图片描述
  • 草的摇摆
    草的摇摆也其实就正常的顶点偏移 ,就是有点不知道3个分向风的参数控制是为啥 可能这样摇摆起来看起来更加自然

    float wind = 0;
    wind += sin(_Time.y * _WindFrequency1 + perGrassPivotPosWS.x * _WindDir1.x + perGrassPivotPosWS.z * _WindDir1.y) * _WindIntensity1;
    wind += sin(_Time.y * _WindFrequency2 + perGrassPivotPosWS.x * _WindDir2.x + perGrassPivotPosWS.z * _WindDir2.y) * _WindIntensity2;
    wind += sin(_Time.y * _WindFrequency3 + perGrassPivotPosWS.x * _WindDir3.x + perGrassPivotPosWS.z * _WindDir3.y) * _WindIntensity3;
    wind  *= input.positionOS.y;
    float3 windOffset = cameraTransformRightWS * wind;
    positionWS += windOffset;
                
    
  • 在这里插入图片描述

  • 行走的轨迹

    1. 一个很有趣的做法,使用拖尾组件 可以形成角色的行动轨迹,然后会缓慢消失的效果
    2. 使用renderfeature将行走的轨迹 绘制下来 传递到草的shader , 根据轨迹进行插值将草压低在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值