沿着色带 x-axis 的线性渐变

linear gradient along the x-axis of a ribbon

我是 webgl 世界的新手所以手​​下留情 ;)

我想要沿着色带 x-axis 的线性渐变。 沿着 y-axis 我找到了 gradientMaterial 但是沿着 x-axis 我还没有找到任何东西。

在此示例中,您可以找到我想要应用渐变的色带。

https://www.babylonjs-playground.com/#E6IX1#137

如果游乐场无法使用: Screen_Shot_2017-08-17_at_09.06.00.png

类似的东西:

https://doc.babylonjs.com/extensions/gradient

但沿着 x-axis

不幸的是,这似乎不是一件简单的事情。

真正定义here的渐变material正在使用世界space中的渐变(对我来说似乎没那么有用,但我知道什么)。这意味着当您移动对象时,渐变将保持在世界中心。

我会将 the fragment shader 中的这一行从

更改为纹理 space
float h = normalize(vPositionW).y + offset;

至此

float h = normalize(vDiffuseUV).y + offset;

这样您就可以通过更改模型的纹理坐标来设置方向。它会让你在色带周围流动渐变,沿着更多色带之类的东西向下流动。

不幸的是,AFAIK Babylon 需要纹理才能使用纹理坐标,至少查看那里的代码您可以看到只有在设置 DIFFUSE 时才会包含纹理坐标,然后假定需要纹理

#ifdef DIFFUSE
varying vec2 vDiffuseUV;
uniform sampler2D diffuseSampler;
uniform vec2 vDiffuseInfos;
#endif

您可以在 Babylon 中制作自定义着色器并提供您自己的数据,但这超出了我的 babylon 技能范围。我能找到的所有教程都需要安装大型环境并从打字稿构建。

一个更简单的解决方案可能只是用您的 2 种颜色创建一个小的渐变纹理并设置 vScalevOffset 以扩展 UV 坐标。

// Using a Canvas because Babylon doesn't support all texture features
// on all types of textures :(
const rampWidth = 1;
const rampHeight = 2;
const tex = new BABYLON.DynamicTexture("dyntex", {width:rampWidth, height:rampHeight}, scene, true);
const ctx = tex.getContext(); 
ctx.fillStyle = "#15A4FA";
ctx.fillRect(0, 0, 1, 1);
ctx.fillStyle = "blue";
ctx.fillRect(0, 1, 1, 1);
tex.vOffset = .5 / (rampHeight);
tex.vScale  = (rampHeight - 1) / rampHeight;
tex.update(false);
mat.diffuseTexture = tex;

现在它在 UV space 中,您可以 see it follows the contours of the ribbon 这不是您想要的,但这是色带的默认 UV 坐标。

要解决此问题,您需要调整 UV 坐标。

var sphere = BABYLON.MeshBuilder.CreateRibbon("sph", {pathArray: paths}, scene);

// get the positions so we can compute the extents
{
    const positions = sphere.getVerticesData(BABYLON.VertexBuffer.PositionKind);
    let min = positions.slice(0, 3);
    let max = positions.slice(0, 3);
    const numVerts = positions.length / 3;
    for (let i = 1; i < numVerts; ++i) {
        const offset = i * 3;
        for (let j = 0; j < 3; ++j) {
            min[j] = Math.min(min[j], positions[offset + j]);
            max[j] = Math.max(max[j], positions[offset + j]);
        }
    }
    // now update the UVs
    const range = [
        max[0] - min[0],
        max[1] - min[1],
        max[2] - min[2],
    ];
    const uvs = new Float32Array(numVerts * 2);
    for (let i = 0; i < numVerts; ++i) {
        const positionOffset = i * 3;
        const uvOffset = i * 2;
        for (let j = 0; j < 2; ++j) {
            uvs[uvOffset + j] = (positions[positionOffset + j] - min[j]) / range[j];
        }
    }
    sphere.setVerticesData(BABYLON.VertexBuffer.UVKind, uvs);
}

而且我还把贴图改成水平的

// Using a Canvas because Babylon doesn't support all texture features
// on all types of textures :(
const rampWidth = 2;
const rampHeight = 1;
const tex = new BABYLON.DynamicTexture("dyntex", {width:rampWidth, height:rampHeight}, scene, true);
const ctx = tex.getContext(); 
ctx.fillStyle = "#15A4FA";
ctx.fillRect(0, 0, 1, 1);
ctx.fillStyle = "blue";
ctx.fillRect(1, 0, 1, 1);
tex.uOffset = 0.5 / (rampWidth);
tex.uScale  = (rampWidth - 1) / rampWidth;
tex.update(false);
mat.diffuseTexture = tex;

https://www.babylonjs-playground.com/#QS8RP8#2

请注意,因为我们将纹理用作渐变,所以我们需要稍微缩放和偏移 UV 坐标。在 BABYLON 中,有一个选项可以使用 texture.uScaletexture.uOffset 和相应的 v 版本。这些设置有效地操纵着色器内的纹理坐标

coordToUse = coordFromBuffer * scale + offset;

我们需要这样做的原因是 WebGL 的纹理坐标参考了像素的边缘,因此假设您有一个 2x1 像素的纹理。 0 和 1 引用了这部分纹理。

0               1
|               |
V               v
+-------+-------+
|       |       |
|       |       |
|       |       |
+-------+-------+

假设当您将纹理用作渐变时,您将获得的左像素为红色,右像素为蓝色

+-------+-------+
|       |       |
|rrrr...|...bbbb|
|       |       |
+-------+-------+

其中 r 是红色,b 是蓝色,.... 是两者之间的混合区域。我们只想使用 2 之间的区域,因为那是 梯度.

这个代码

tex.vOffset = .5 / (rampHeight);
tex.vScale  = (rampHeight - 1) / rampHeight;

缩放 UV 坐标,因此它们只使用中间部分。基本上我们减去 1 个像素并移动半个像素。如果您注释掉这两行,您会看到形状的边缘、rrrr 和 bbbb 部分有纯色边框。

如果 BABYLON 没有提供该选项,我们要么必须编写自己的着色器来添加它,要么在制作我们放入缓冲区的 UV 坐标时更改我们的计算。在着色器中执行此操作(使用 BABYLON 方式或使用自定义着色器)的优点是偏移量和比例需要根据纹理的大小而不同,因此我们必须更新缓冲区中的所有 UV如果我们没有在着色器中调整 UV 坐标,我们随时更改渐变纹理的大小

不,如果您不熟悉 WebGL 主题,我建议您阅读 these tutorials。然后,如果你深入研究 babylon.js 来源,希望它会更清楚发生了什么。