三个js和glsl多重纹理渲染问题

问题描述 投票:0回答:1

你好,我正在尝试使用 React、三纤维 js 和 glsl 做 3D 模型配置示例

必须有多个贴纸,并且贴纸必须可以动态排列(位置、缩放、旋转),以便 我决定使用着色器

当所有贴纸都提前设置好就没有问题了

但问题是贴纸会被客户添加,我找不到出路

目前的问题是我无法在 glsl 中使用动态索引 for for 循环

有人知道我怎样才能做到这一点吗 非常感谢提前

这就是我在画布上的内容

return (
  <mesh geometry={nodes.cloth_parent.geometry}>
    <shaderMaterial
      uniforms={{
        leftSleeveColor: { value: new Vector3(...leftSleeveColor) },
        blendFactor: { value: blendFactor },
        lightPosition: { value: new Vector3(2.0, 2.0, 2.0) }, // Lighting position
        diffuseMap: { value: diffuseMapTexture },
        normalMap: { value: normalMapTexture },
        numStickers: { value: stickers.length },
        stickerTextures: { value: stickerTextures },
        stickerPositions: {
          value: stickers.map(
            (sticker) => new Vector2(...sticker.position)
          ),
        },
        stickerScales: {
          value: stickers.map((sticker) => sticker.scale),
        },
        stickerRotations: {
          value: stickers.map((sticker) => sticker.rotation),
        },
      }}
      vertexShader={vertexShader}
      fragmentShader={fragmentShader}
    />
  </mesh>
);

这就是我的片段

const fragmentShader = `
    uniform vec3 leftSleeveColor;
    uniform float blendFactor; // Dynamic blend factor
    uniform vec3 lightPosition;
    uniform sampler2D diffuseMap;
    uniform sampler2D normalMap; // Normal map for fabric texture
    uniform int numStickers; // Number of stickers
    uniform sampler2D stickerTextures[10]; // Array of up to 10 stickers
    uniform vec2 stickerPositions[10]; // Array of sticker positions
    uniform float stickerScales[10]; // Array of sticker scales
    uniform float stickerRotations[10]; // Array of sticker rotations

    varying vec2 vUv;
    varying vec3 vNormal;
    varying vec3 vPosition;

    void main() {
      // Sample the normal map and adjust normals
      vec3 normalFromMap = texture2D(normalMap, vUv).rgb;
      normalFromMap = normalize(normalFromMap * 2.0 - 1.0);
      vec3 adjustedNormal = normalize(vNormal + normalFromMap);

      // Lighting calculation
      vec3 lightDir = normalize(lightPosition - vPosition);
      float lightIntensity = max(dot(adjustedNormal, lightDir), 0.0);
      vec3 baseColor = texture2D(diffuseMap, vUv).rgb * lightIntensity;

      // Apply the red color to the left sleeve
      if (vUv.x > 0.1 && vUv.x < 0.3 && vUv.y > 0.6 && vUv.y < 0.8) {
        vec3 sleeveColorWithLight = leftSleeveColor * lightIntensity;
        baseColor = mix(baseColor, sleeveColorWithLight, blendFactor);
      }

      // Loop through each sticker and apply transformations
      for (int i = 0; i < numStickers; i++) {
        vec2 translatedUV = (vUv - stickerPositions[i]) / stickerScales[i];

        // Rotate the UVs around the sticker's center
        vec2 stickerCenter = vec2(0.5, 0.5);
        vec2 centeredUV = translatedUV - stickerCenter;
        float cosTheta = cos(stickerRotations[i]);
        float sinTheta = sin(stickerRotations[i]);
        mat2 rotationMatrix = mat2(cosTheta, -sinTheta, sinTheta, cosTheta);
        vec2 rotatedUV = rotationMatrix * centeredUV + stickerCenter;

        // Sample the sticker texture using the transformed UVs
        vec4 stickerColor = texture2D(stickerTextures[i], rotatedUV);

        // Apply the sticker based on alpha and lighting
        if (rotatedUV.x >= 0.0 && rotatedUV.x <= 1.0 && rotatedUV.y >= 0.0 && rotatedUV.y <= 1.0) {
          vec3 stickerEffect = stickerColor.rgb * lightIntensity;
          baseColor = mix(baseColor, stickerEffect, stickerColor.a);
        }
      }

      gl_FragColor = vec4(baseColor, 1.0); // Output final color with lighting and stickers
    }
  `;

解决方案 我为贴纸创建了片段模板,并将它们动态添加到片段中,并且我还在 startTransition 中添加了添加贴纸和更新贴纸功能

return (
      <Suspense fallback={<p>wait please</p>}>
        <mesh geometry={nodes.cloth_parent.geometry}>
          <shaderMaterial
            uniforms={{
              leftSleeveColor: { value: new Vector3(...leftSleeveColor) },
              blendFactor: { value: blendFactor },
              lightPosition: { value: new Vector3(2.0, 2.0, 2.0) }, // Lighting position
              diffuseMap: { value: diffuseMapTexture },
              normalMap: { value: normalMapTexture },
              ...stickers.reduce(
                (acc, sticker, i) => ({
                  ...acc,
                  [`stickerTexture${i}`]: { value: stickerTextures[i] },
                  [`stickerPosition${i}`]: {
                    value: new Vector2(...sticker.position),
                  },
                  [`stickerScale${i}`]: { value: sticker.scale },
                  [`stickerRotation${i}`]: { value: sticker.rotation },
                }),
                {}
              ),
            }}
            vertexShader={vertexShader}
            fragmentShader={fragmentShader}
          />
        </mesh>
      </Suspense>
    );
  }

const fragmentShader = `
    uniform vec3 leftSleeveColor;
    uniform float blendFactor;
    uniform vec3 lightPosition;
    uniform sampler2D diffuseMap;
    uniform sampler2D normalMap;
    varying vec2 vUv;
    varying vec3 vNormal;
    varying vec3 vPosition;

    ${stickerFragmentsUniforms.join(
      "\n"
    )} // Insert dynamically generated uniforms

    void main() {
      // Sample the normal map and adjust normals
      vec3 normalFromMap = texture2D(normalMap, vUv).rgb;
      normalFromMap = normalize(normalFromMap * 2.0 - 1.0);
      vec3 adjustedNormal = normalize(vNormal + normalFromMap);

      // Lighting calculation
      vec3 lightDir = normalize(lightPosition - vPosition);
      float lightIntensity = max(dot(adjustedNormal, lightDir), 0.0);

      // Sample base diffuse map texture
      vec3 baseColor = texture2D(diffuseMap, vUv).rgb * lightIntensity;

      // Apply the red color to the left sleeve
      if (vUv.x > 0.1 && vUv.x < 0.3 && vUv.y > 0.6 && vUv.y < 0.8) {
        vec3 sleeveColorWithLight = leftSleeveColor * lightIntensity;
        baseColor = mix(baseColor, sleeveColorWithLight, blendFactor);
      }

      ${stickerFragments.join(
        "\n"
      )} // Insert dynamically generated sticker logic

      gl_FragColor = vec4(baseColor, 1.0);
    }
  `;
reactjs glsl shader fragment-shader react-three-fiber
1个回答
0
投票

解决方案 我为贴纸创建了片段和统一模板,并将它们动态添加到片段中,并且我还在 startTransition 中添加了添加贴纸和更新贴纸功能

片段模板:

const createStickerFragment = (index) => `
  vec2 translatedUV${index} = (vUv - stickerPosition${index}) / stickerScale${index};
  vec2 stickerCenter${index} = vec2(0.5, 0.5);
  vec2 centeredUV${index} = translatedUV${index} - stickerCenter${index};
  float cosTheta${index} = cos(stickerRotation${index});
  float sinTheta${index} = sin(stickerRotation${index});
  mat2 rotationMatrix${index} = mat2(cosTheta${index}, -sinTheta${index}, sinTheta${index}, cosTheta${index});
  vec2 rotatedUV${index} = rotationMatrix${index} * centeredUV${index} + stickerCenter${index};
  vec4 stickerColor${index} = texture2D(stickerTexture${index}, rotatedUV${index});

  if (rotatedUV${index}.x >= 0.0 && rotatedUV${index}.x <= 1.0 && rotatedUV${index}.y >= 0.0 && rotatedUV${index}.y <= 1.0) {
    baseColor = mix(baseColor, stickerColor${index}.rgb * lightIntensity, stickerColor${index}.a);
  }
`;

统一模板:

const createStickerFragmentUniforms = (index) => `
  uniform sampler2D stickerTexture${index};
  uniform float stickerScale${index};
  uniform vec2 stickerPosition${index};
  uniform float stickerRotation${index};
`;

在不使用 startTransition 暂停 dom 的情况下添加贴纸:

const addSticker = () => {
    startTransition(() => {
      setStickers((currentStickers) => [
        ...currentStickers,
        { texture: "/t1.png", position: [0.5, 0.5], scale: 1.0, rotation: 0.0 },
      ]);
    });
  };

如何生成贴纸动态声明

const stickerFragmentsUniforms = stickers.map((_, index) =>
    createStickerFragmentUniforms(index)
  );
  const stickerFragments = stickers.map((_, index) =>
    createStickerFragment(index)
  );

如何转动着色器中的每个贴纸:

<Suspense fallback={<p>wait please</p>}>
        <mesh geometry={nodes.cloth_parent.geometry}>
          <shaderMaterial
            uniforms={{
              leftSleeveColor: { value: new Vector3(...leftSleeveColor) },
              blendFactor: { value: blendFactor },
              lightPosition: { value: new Vector3(2.0, 2.0, 2.0) }, // Lighting position
              diffuseMap: { value: diffuseMapTexture },
              normalMap: { value: normalMapTexture },
              ...stickers.reduce(
                (acc, sticker, i) => ({
                  ...acc,
                  [`stickerTexture${i}`]: { value: stickerTextures[i] },
                  [`stickerPosition${i}`]: {
                    value: new Vector2(...sticker.position),
                  },
                  [`stickerScale${i}`]: { value: sticker.scale },
                  [`stickerRotation${i}`]: { value: sticker.rotation },
                }),
                {}
              ),
            }}
            vertexShader={vertexShader}
            fragmentShader={fragmentShader}
          />
        </mesh>
      </Suspense>

如何将制服和片段嵌入到fragmentshader中:

const fragmentShader = `
    uniform vec3 leftSleeveColor;
    uniform float blendFactor;
    uniform vec3 lightPosition;
    uniform sampler2D diffuseMap;
    uniform sampler2D normalMap;
    varying vec2 vUv;
    varying vec3 vNormal;
    varying vec3 vPosition;

    ${stickerFragmentsUniforms.join(
      "\n"
    )} // Insert dynamically generated uniforms

    void main() {
      // Sample the normal map and adjust normals
      vec3 normalFromMap = texture2D(normalMap, vUv).rgb;
      normalFromMap = normalize(normalFromMap * 2.0 - 1.0);
      vec3 adjustedNormal = normalize(vNormal + normalFromMap);

      // Lighting calculation
      vec3 lightDir = normalize(lightPosition - vPosition);
      float lightIntensity = max(dot(adjustedNormal, lightDir), 0.0);

      // Sample base diffuse map texture
      vec3 baseColor = texture2D(diffuseMap, vUv).rgb * lightIntensity;

      // Apply the red color to the left sleeve
      if (vUv.x > 0.1 && vUv.x < 0.3 && vUv.y > 0.6 && vUv.y < 0.8) {
        vec3 sleeveColorWithLight = leftSleeveColor * lightIntensity;
        baseColor = mix(baseColor, sleeveColorWithLight, blendFactor);
      }

      ${stickerFragments.join(
        "\n"
      )} // Insert dynamically generated sticker logic

      gl_FragColor = vec4(baseColor, 1.0);
    }
  `;
© www.soinside.com 2019 - 2024. All rights reserved.