我需要在顶点着色器中旋转和zoom UV,以便旋转的纹理填充其可用边界框.以下测试实现成功地旋转和自动zoom 纹理,但随着旋转值的增加,图像会倾斜/扭曲.

我正在考虑纹理的长宽比来进行自动zoom ,但我肯定在旋转步骤中遗漏了一些东西.

This question似乎相关,但我无法将建议的解决方案转换到我的顶点着色器,因为我不知道Three.js如何在引擎盖下工作.

如有任何帮助,不胜感激!

const VERTEX_SHADER = (`
  varying vec2 vUv;
  uniform vec2 tSize; // Texture size (width, height)
  uniform float rotation; // Rotation angle in radians

  vec2 rotateAndScaleUV(vec2 uv, float angle, vec2 tSize) {

    vec2 center = vec2(0.5);

    // Step 1: Move UVs to origin for rotation
    vec2 uvOrigin = uv - center;

    // Step 2: Apply rotation matrix
    float cosA = cos(rotation);
    float sinA = sin(rotation);
    mat2 rotMat = mat2(cosA, -sinA, sinA, cosA);
    vec2 rotatedUv = rotMat * uvOrigin;

    // Step 3: Auto-scale to fill available space
    float aspectRatio = tSize.x / tSize.y;
    float scale = 1.0 / max(abs(cosA) + abs(sinA) / aspectRatio, abs(sinA) + abs(cosA) * aspectRatio);
    return rotatedUv * scale + center; // Scale and move back to correct position

  }

  void main() {
    vUv = rotateAndScaleUV(uv, rotation, tSize);
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`);

// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);

// Load an image and create a mesh that matches its aspect ratio
new THREE.TextureLoader().load('https://images.unsplash.com/photo-1551893478-d726eaf0442c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzMjM4NDZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MDcyNDI0MTB8&ixlib=rb-4.0.3&q=80&w=400', texture => {
  
  texture.minFilter = THREE.LinearFilter;
  texture.generateMipMaps = false;
  
  const img = texture.image;
  const aspectRatio = img.width / img.height;
  
  // Create geometry with the same aspect ratio
  const geometry = new THREE.PlaneGeometry(aspectRatio, 1);
  
  // Shader material
  const shaderMaterial = new THREE.ShaderMaterial({
    uniforms: {
      textureMap: { value: texture },
      tSize: { value: [img.width, img.height] },
      rotation: { value: 0 }
    },
    vertexShader: VERTEX_SHADER,
    fragmentShader: `
      uniform sampler2D textureMap;
      varying vec2 vUv;
      void main() {
        gl_FragColor = texture2D(textureMap, vUv);
      }
    `
  });
  
  camera.position.z = 1;

  // Create and add mesh to the scene
  const mesh = new THREE.Mesh(geometry, shaderMaterial);
  scene.add(mesh);
  
  // UI controls
  document.getElementById('rotation').addEventListener('input', e => {
    shaderMaterial.uniforms.rotation.value = parseFloat(e.target.value);
    renderer.render(scene, camera);
  });
  
  window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.render(scene, camera);
  }, false);
  
  renderer.render(scene, camera);
  
});
body {margin: 0; color: grey;}
#container {
  width: 100vw;
  height: 100vh;
}
#ui {
  position: absolute;
  top: 5%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
}
<script src="https://cdn.jsdelivr.net/npm/three-js@79.0.0/three.min.js"></script>
<div id="container"></div>
<div id="ui">
  <label for="rotation">Rotation:</label>
  <input type="range" id="rotation" min="-1" max="1" step="0.001" value="0">
</div>

推荐答案

关闭的内容:

  • 一旦你集中注意力,你就需要集中精力;
  • 你的比例是MAD的不同原因,先乘后加,从原点0, 0开始按比例乘,然后加,然后居中,你需要在比例之前居中.

const VERTEX_SHADER = (`
  varying vec2 vUv;
  uniform vec2 tSize; // Texture size (width, height)
  uniform float rotation; // Rotation angle in radians

  vec2 rotateAndScaleUV(vec2 uv, float angle, vec2 tSize) {

    vec2 p = uv;
    float mid = 0.5;    
    float aspect = tSize.x / tSize.y;
    
    float cosA = cos(rotation);
    float sinA = sin(rotation);
    float scale = 1.0 / max(abs(cosA) + abs(sinA) / aspect, abs(sinA) + abs(cosA) * aspect);
    
    mat2 rotMat = mat2(cosA, -sinA, sinA, cosA);

    p -= vec2(mid);
    p *= scale;
    p.y *= 1.0 / aspect;
    p *= rotMat;
    
    p.y *= aspect;
    p += vec2(mid);

    return p;

  }

  void main() {
    vUv = rotateAndScaleUV(uv, rotation, tSize);
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`);

// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);

// Load an image and create a mesh that matches its aspect ratio
new THREE.TextureLoader().load('https://images.unsplash.com/photo-1551893478-d726eaf0442c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzMjM4NDZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MDcyNDI0MTB8&ixlib=rb-4.0.3&q=80&w=400', texture => {
  
  texture.minFilter = THREE.LinearFilter;
  texture.generateMipMaps = false;
  
  const img = texture.image;
  const aspectRatio = img.width / img.height;
  
  // Create geometry with the same aspect ratio
  const geometry = new THREE.PlaneGeometry(aspectRatio, 1);
  
  // Shader material
console.log(img.width, img.height);
  const shaderMaterial = new THREE.ShaderMaterial({
  
    uniforms: {
      textureMap: { value: texture },
      tSize: { value: [img.width, img.height] },
      rotation: { value: 0 }
    },
    vertexShader: VERTEX_SHADER,
    fragmentShader: `
      uniform sampler2D textureMap;
      varying vec2 vUv;
      void main() {
        gl_FragColor = texture2D(textureMap, vUv);
      }
    `
  });
  
  camera.position.z = 1;

  // Create and add mesh to the scene
  const mesh = new THREE.Mesh(geometry, shaderMaterial);
  scene.add(mesh);
  
  // UI controls
  document.getElementById('rotation').addEventListener('input', e => {
    shaderMaterial.uniforms.rotation.value = parseFloat(e.target.value);
    renderer.render(scene, camera);
  });
  
  window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.render(scene, camera);
  }, false);
  
  renderer.render(scene, camera);
  
});
body {margin: 0; color: grey;}
#container {
  width: 100vw;
  height: 100vh;
}
#ui {
  position: absolute;
  top: 5%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
}
<script src="https://cdn.jsdelivr.net/npm/three-js@79.0.0/three.min.js"></script>
<div id="container"></div>
<div id="ui">
  <label for="rotation">Rotation:</label>
  <input type="range" id="rotation" min="-1" max="1" step="0.001" value="0">
</div>

Javascript相关问答推荐

无法将nPM simplex-noise包导入在JS项目中工作

如何使用JavaScript用等效的功能性HTML替换标记URL格式?

Google图表时间轴—更改hAxis文本 colored颜色

. NET中Unix时间转换为日期时间的奇怪行为

为什么ngModel不能在最后一个版本的Angular 17上工作?'

单击ImageListItemBar的IconButton后,在Material—UI对话框中显示图像

引用在HTMLAttributes<;HTMLDivElement>;中不可用

如何添加绘图条形图图例单击角形事件

为什么useState触发具有相同值的呈现

在WordPress中使用带有WPCode的Java代码片段时出现意外令牌错误

在HTML语言中调用外部JavaScript文件中的函数

一个实体一刀VS每个实体多刀S

如何使用基于promise (非事件emits 器)的方法来传输数据?

react -原生向量-图标笔划宽度

JWT Cookie安全性

顶点图使用组标签更新列之间的条宽

如何使用[ModelJSON,ArrayBuffer]调用tf.loadGraphModelSync

在单击按钮时生成多个表单时的处理状态

我怎样才能点击一个元素,并获得一个与 puppeteer 师导航页面的URL?

使用Java脚本在div中创建新的span标记