Three.js在着色器中获取顶点的局部位置,甚至是我需要的吗?

时间:2018-04-21 00:16:06

标签: three.js glsl

我正在尝试将这种渲染草的技术应用到我的three.js应用程序中。

http://davideprati.com/demo/grass/

在0号位置的水平地形上,一切看起来都非常棒!

问题是,我的应用程序(游戏)的地形由高度图修改,因此该地形上的极少数(如果有)位置位于y位置。

看起来这个顶点着色器动画代码假设草对象位于y位置0,以便下面的顶点着色器代码按预期工作:

    if (pos.y > 1.0) {
        float noised = noise(pos.xy);
        pos.y += sin(globalTime * magnitude * noised);
        pos.z += sin(globalTime * magnitude * noised);
        if (pos.y > 1.7){
            pos.x += sin(globalTime * noised);
        }
    }

这种情况假设地形是平坦的并且位于0处,因此只有地面上方的顶点才有动画效果。好吧..嗯..因为所有顶点都高于1,高度图(大多数情况下),会出现一些奇怪的效果,例如草地滑动到整个地方大声笑。

有没有办法做到这一点,我可以更多地根据精灵指定y位置阈值而不是世界位置?还是有更好的方法来处理这个“滑倒”的问题?

对于着色器代码=]

,我是一个极端的noobie

非常感谢任何帮助。

我不知道我在做什么。

编辑*好吧,我认为问题是我正在根据它所在的地形的y位置改变合并到主草容器几何体中的每个网格的y位置。我想着色器正在查看局部位置,但由于几何体本身垂直位移,着色器不知道如何补偿。嗯...

好的,我做了一个演示问题的小提琴: https://jsfiddle.net/titansoftime/a3xr8yp7/

将#128行上的值更改为1而不是2,一切都很好。不确定如何解决这个问题。

另外,我不知道为什么颜色会这样做,它们在我的应用程序中看起来很好。

1 个答案:

答案 0 :(得分:1)

如果我正确理解了这个问题:

你是在寻求“本地”的立场。可以说,单股草是一条狭长的条带,有一些高度段。

如果您希望这种模块化,易于扩展等,这很可能会在0-1范围内向某个方向延伸。假设它沿着该方向有四个段,这将产生坐标为[0.0,0.333,0.666,1.0]的顶点。它比任意范围稍微有点意义,因为很容易推断0是磨削的,1是刀尖。

这是“本地”或模型空间。将此值与modelMatrix相乘后,您将其转换为世界空间(称之为localToWorld)。

在着色器中,它可能看起来像这样

void main(){
   vec4 localPosition = vec4( position, 1.);
   vec4 worldPosition = modelMatrix * localPosition;
   vec4 viewPosition = viewMatrix * worldPosition;
   vec4 projectedPosition = projectionMatrix * viewPosition; //either orthographic or perspective

   gl_Position = projectedPosition;
}

这是您转换的经典“你有一个场景图节点”。根据您为网格position设置的内容,rotationscale vec4 worldPosition会有所不同,但本地位置始终相同。如果某些东西是底部或顶部,你无法单独判断该值,任何值都是可行的,因为你的地形可以是任何东西。

使用这种方法,您可以编写着色器和逻辑,说明如果顶点高度为0(或小于某个epsilon),则不进行动画处理。

所以这给我们带来了一些逻辑,它在一些假定的空间中起作用(你有1.0和1.7的规则)。

因为您正在翻译几何图形并将它们合并,所以您不再具有此用户友好空间即模型空间。现在这些刀片很可能会跳过local2world转换(它很可能最终只是一个单位矩阵)。

这明显地弄乱了你选择顶点的逻辑。

如果您必须采用分配它们的方法,那么您需要另一个频道来表示该局部空间的含义,即使您只将它用于该动画

已存在两个合适的通道 - 紫外线和顶点颜色。 Uv你可以想象在另一个空间中有另一个平面网格,它映射到你正在渲染的网格。但在这种特殊情况下,您似乎可以使用自定义属性aBladeHeight,例如浮点数。

void main(){
     vec4 worldPosition = vec4(position, 1.); //you "burnt/baked" this transformation in, so no need to go from local to world in the shader

     vec2 localPosition = uv; //grass in 2d, not transformed to your terrain
     //this check knows whats on the bottom of the grass
     //rather than whats on the ground (has no idea where the ground is)
     if(localPosition.y){
        //since local does not exist, the only space we work in is world
        //we apply the transformation in that space, but the filter 
        //is the check above, in uv space, where we know whats the bottom, whats the top
        worldPosition.xy += myLogic();
     } 
     gl_Position = projectionMatrix * viewMatrix * worldPosition;
}

模仿“本地空间”

void main(){
   vec4 localSpace = vec4(uv,0.,1.);
   gl_Position = projectionMatrix * modelViewMatrix * localSpace;
}

所有刀片都会互相重叠。

修改

通过实例化,着色器看起来像这样:

attribute vec4 aInstanceMatrix0; //16 floats to encode a matrix4
attribute vec4 aInstanceMatrix1;
attribute vec4 aInstanceMatrix2;
//attribute vec4 aInstanceMatrix3; //but one you know will be 0,0,0,1 so you can pack in the first 3


void main(){
  vec4 localPos = vec4(position, 1.); //the local position is intact, its the normalized 0-1 blade

  //do your thing in local space
  if(localPos.y > foo){
     localPos.xz += myLogic();
  }

  //notice the difference, instead of using the modelMatrix, you use the instance attributes in it's place
  mat4 localToWorld = mat4(
    aInstanceMatrix0,
    aInstanceMatrix1,
    aInstanceMatrix2,
    //aInstanceMatrix3
    0. , 0. , 0. , 1. //this is actually wrong i think, it should be the last column not row, but for illustrative purposes, 
  ); 

  //to pack it more effeciently the rows would look like this
  // xyz w
  // xyz w
  // xyz w  
  // 000 1
  // off the top of my head i dont know what the correct code is
  mat4 foo = mat4( 
    aInstanceMatrix0.xyz, 0.,
    aInstanceMatrix1.xyz, 0.,
    aInstanceMatrix2.xyz, 0.,
    aInstanceMatrix0.w, aInstanceMatrix1.w, aInstanceMatrix2.w, 1.
)

  //you can still use the modelMatrix with this if you want to move the ENTIRE hill with all the grass with .position.set()
  vec4 worldPos = localToWorld * localPos; 
  gl_Position = projectionMatrix * viewMatrix * worldPos;
}