three.js如何修改3d模型贴图?

230 2024-03-09 00:25

一、three.js如何修改3d模型贴图?

打开电脑之后,我们点击打开右上角的wonds之后点击打开 模型贴图之后,选择3d影视之后,点击键盘的打米键,即可修改3d隐形贴贴图

二、three.js 3d模型用什么做的?

three.js 3d模型是使用Javascript编写的。因为three.js 是一个基于WebGL的开源Javascript库,它提供了各种功能来创建和渲染3D场景,包括模型加载、材质、照明和相机等。所以使用Javascript编写three.js 3d模型更加方便快捷。除此之外,还可以使用其他3D建模软件制作模型并导出为three.js支持的格式,如blender、3ds max、Maya等。这些软件可以更好地实现模型的细节和复杂度,但相应的需要更多的时间和专业知识。

三、在webgl、three.js如何对3D建模做剖切的效果?大概什么原理?

原文:How to Build a Color Customizer App for a 3D Model with Three.js

本文将阐述如何基于 Three.js 创建一个完整的 3D 模型(椅子)颜色自定义应用。

马上体验:3D Model Color Customizer App with Three.js

快速介绍

该工具的灵感来源于 Vans shoe customizer,并采用优秀的 JavaScript 3D 库 Three.js 实现。

阅读本文的前提是已掌握 JavaScript、HTML 和 CSS。

为了能让你确切学到东西,而不是单纯地粘贴/复制。本文不按常规出牌,在一开始就给出全部 CSS。CSS 起到装扮应用的作用,即仅专注于 UI。每当我们粘贴部分 HTML 时,都会讲解相应 CSS 的作用。

Part 1: 3D 模型

你可以完全跳过本节,但它可以让你对这一切有更深入的了解。

这不是一篇关于 3D 建模的教程,但我将阐述如何在 Blender 中设置模型,这有助于你创建属于自己的模型、修改网上的免费模型或指点他人调试。以下是创作 3D 模型——椅子的一些经验。

尺寸(Scale)

模型需设置为符合真实世界的尺寸。我也不知道这是否重要,但感觉没问题,为什么不这样做呢?

分层和命名约定(Layering and naming conventions)

这部分很重要:物体中每个需要独立控制的元素都必须是 3D 场景中独立的对象。这些对象也必须拥有唯一的名字。这里有 back(背部)、base(底座)、cushions(坐垫)、legs(椅腿)和 supports(支架)。若有三个元素都命名为 supports,那么 Blender 会将它们命名为 supportssupports.001supports.002。这没问题,因为我们可以在 JavaScript 中使用 includes("supports") 找到它们。

落点(Placement)

模型应放置在场景的原点,并落在地板上。另外,最好能面向正确的方向,但这可通过 JavaScript 旋转易实现。

导出设置(Setting up for export)

导出前,要使用 Blender 的 Smart UV unwrap。在此不深入细节,总之这会让纹理可以保持宽高比不变,保证不会在包裹模型中因各类形状而产生怪异的拉伸(建议你制作自己的模型时才仔细研究它)。

确保所有对象应用 transformations(译者注:即将缩放转为对象实际尺寸)。

文件格式(File Format)

显然 Three.js 支持很多 3D 对象文件格式,但它推荐的格式之一是 glTF(.glb)。而且 Blender 也支持导出该格式。

Part 2:建立环境

Fork 这个 pen(译者注:即 codepen 的一个案例),或创建一个 pen 并从其中复制 CSS。这是一个含有本教程所有 CSS 的空白 pen。

3D Chair Customizer Tutorial - Blank

若不选择 fork,也需要复制 HTML。这包含响应式 meta 标签和 Google 字体。

本教程使用了 3 个依赖,我在它们各自上方写有描述用途的注释。

<!-- The main Three.js file -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.min.js'></script>

<!-- This brings in the ability to load custom 3D objects in the .gltf file format. Blender allows the ability to export to this format out the box -->
<script src='https://cdn.jsdelivr.net/gh/mrdoob/Three.js@r92/examples/js/loaders/GLTFLoader.js'></script>

<!-- This is a simple to use extension for Three.js that activates all the rotating, dragging and zooming controls we need for both mouse and touch, there isn't a clear CDN for this that I can find -->
<script src='https://threejs.org/examples/js/controls/OrbitControls.js'></script>

引入 canvas 标签。整个 3D 体验将渲染于此,而其余 HTML 标签作为 UI 辅助于它。将 canvas 放在 HTML 底部(脚本前)。

<!-- The canvas element is used to draw the 3D scene -->
<canvas id="c"></canvas>

现在为 Three.js 创建一个 scene。

// Init the scene
const scene = new THREE.Scene();

接着引用 canvas 元素:

const canvas = document.querySelector('#c');

Three.js 需要具备一些元素才能跑起来。第一个是 scene,第二个是 renderer。在 canvas 变量下方添加它。创建一个 WebGLRenderer,传入 canvas 和选项参数(抗齿距,使 3D 模型的边缘更光滑)。

// Init the renderer
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});

然后将 renderer 插入到 body 元素(译者注:此行代码可省略)。:

document.body.appendChild(renderer.domElement);
为 canvas 编写的 CSS 仅是将其拉伸至 body 的 100% 宽高,因此整个页面目前是黑色的(即 canvas 现在是黑色)。

虽然场景目前漆黑一片,但我们走在正确的道路上。

接着 Three.js 需要一个更新循环,这是一个在每帧都会执行的函数,对运行我们的应用程序起到重要作用。我们将更新函数命名为 animate(),并将其放置在 JavaScript 代码的最底部。

function animate() {
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

animate();

请注意,上面代码了引用摄像机(camera),但我们仍未添加它。

在 JavaScript 代码顶部,添加一个名为 cameraFar 的变量。当我们添加 camera 到 scene 时,其默认位置是 0,0,0。但这可是椅子的位置!因此 cameraFar 变量是告诉 camera 应离此多远,以确保能看到椅子。

var cameraFar = 5;

animate() 函数上方添加 camera。

// Add a camera
var camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = cameraFar;
camera.position.x = 0;

这是一个透视摄像机,其参数为 50 视场(field of view,fov),宽高比和默认的裁剪区域。裁剪区域指定了可视区域的前后边界。当然,这些都不是本应用关心的事情。(译者注:可参考《Three.js 现学现卖》

我们的场景仍然是黑色,下面设置背景色。

在顶部的 scene 变量上方,添加背景色变量 BACKGROUND_COLOR

const BACKGROUND_COLOR = 0xf1f1f1;
注意我们这里的十六进制是使用 0x 而不是 #。这不是字符串,而是以 0x 开头的整数。

在 scence 变量下方,更新 scene 的背景色,并在远处添加同样颜色的雾,旨在隐藏地板的边界。

const BACKGROUND_COLOR = 0xf1f1f1;

// Init the scene
const scene = new THREE.Scene();

// Set background
scene.background = new THREE.Color(BACKGROUND_COLOR );
scene.fog = new THREE.Fog(BACKGROUND_COLOR, 20, 100);

现在仍是一个空白的世界,没有东西,没有投影。是时候加载模型了。

Part 3:加载模型

我们将使用第二个依赖来加载模型。

在此之前,我们先声明引用模型的变量,该变量会被频繁使用。我们将其放在 JavaScript 顶部的 BACKGROUND_COLOR 前。同时,添加该模型的路径。我已对其进行托管,供大家使用。它有 1Mb 左右的大小。

var theModel;
const MODEL_PATH =  "https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/chair.glb";

现在创建一个 loader,并使用其 load 方法。theModel 就是整个场景的 3D 模型。将其设置合适的尺寸大小,这里设为原大小的 2 倍。接着,设置其 y 轴偏移量为 -1,使其往下移动。最后将其添加到场景中。

load 函数的第一个参数是模型的路径,第二个参数是资源加载成功后的回调函数,第三个参数目前是 undefined,但它其实是资源加载期间的回调函数,最后一个参数是报错回调函数。

将这部分代码放在 camera 下方。

// Init the object loader
var loader = new THREE.GLTFLoader();

loader.load(MODEL_PATH, function(gltf) {
  theModel = gltf.scene;

  // Set the models initial scale   
  theModel.scale.set(2,2,2);

  // Offset the y position a bit
  theModel.position.y = -1;

  // Add the model to the scene
  scene.add(theModel);

}, undefined, function(error) {
  console.error(error)
});

此时,你应该看到的是一张被拉伸、黑色且像素化的椅子。看起来很糟糕,但这是正常的,别担心!

除了摄像机,我们还需要光。背景不受光影响,但如果此时添加地板,那么它依然会是黑色。Three.js 有几种类型的光且有丰富的选项可供调整。这里我们添加两种:环境光和定向光。两者的设置是专门适配该应用的,其中包括位置和强度。如果你对它们有使用经验,可以尝试更改。但现在就使用我提供的参数吧。将光放在 loader 下方。

// Add lights
var hemiLight = new THREE.HemisphereLight( 0xffffff, 0xffffff, 0.61 );
    hemiLight.position.set( 0, 50, 0 );
// Add hemisphere light to scene   
scene.add( hemiLight );

var dirLight = new THREE.DirectionalLight( 0xffffff, 0.54 );
    dirLight.position.set( -8, 12, 8 );
    dirLight.castShadow = true;
    dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
// Add directional Light to scene    
scene.add( dirLight );

此时,椅子看起来稍微好一些!到目前为止,JavaScript 如下:

var cameraFar = 5;
var theModel;

const MODEL_PATH =  "https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/chair.glb";

const BACKGROUND_COLOR = 0xf1f1f1;
// Init the scene
const scene = new THREE.Scene();
// Set background
scene.background = new THREE.Color(BACKGROUND_COLOR );
scene.fog = new THREE.Fog(BACKGROUND_COLOR, 20, 100);

const canvas = document.querySelector('#c');

// Init the renderer
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});

document.body.appendChild(renderer.domElement);

// Add a camerra
var camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = cameraFar;
camera.position.x = 0;

// Init the object loader
var loader = new THREE.GLTFLoader();

loader.load(MODEL_PATH, function(gltf) {
  theModel = gltf.scene;

  // Set the models initial scale   
  theModel.scale.set(2,2,2);

  // Offset the y position a bit
  theModel.position.y = -1;

  // Add the model to the scene
  scene.add(theModel);

}, undefined, function(error) {
  console.error(error)
});

// Add lights
var hemiLight = new THREE.HemisphereLight( 0xffffff, 0xffffff, 0.61 );
    hemiLight.position.set( 0, 50, 0 );
// Add hemisphere light to scene   
scene.add( hemiLight );

var dirLight = new THREE.DirectionalLight( 0xffffff, 0.54 );
    dirLight.position.set( -8, 12, 8 );
    dirLight.castShadow = true;
    dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
// Add directional Light to scene    
scene.add( dirLight );

function animate() {
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

animate();

现在看起来如下:

让我们解决像素化和拉伸的问题。Three.js 需要在(视口)改变时更新 canvas 尺寸,其内部分辨率需依赖于 canvas 尺寸和设备屏幕像素比(手机的像素比一般比较高)。

在 JavaScript 代码底部,即调用 animate() 的下方添加该函数。该函数会监听 canvas 的尺寸和 window 的尺寸,并返回一个判断两者是否相同的布尔值。我们将会在 animate 函数内使用该函数,以决定是否需要重新渲染场景(设置场景大小)。该函数还会考虑设备像素比,以确保 canvas 在手机上也清晰。(译者注:建议通过监听 window resize 事件进行判断,且设备像素比上限为 2)

在 JavaScript 底部添加该函数:

function resizeRendererToDisplaySize(renderer) {
  const canvas = renderer.domElement;
  var width = window.innerWidth;
  var height = window.innerHeight;
  var canvasPixelWidth = canvas.width / window.devicePixelRatio;
  var canvasPixelHeight = canvas.height / window.devicePixelRatio;

  const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height;
  if (needResize) {
    renderer.setSize(width, height, false);
  }
  return needResize;
}

现在更新 animate 函数后看起来如下:

function animate() {
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
  
  if (resizeRendererToDisplaySize(renderer)) {
    const canvas = renderer.domElement;
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();
  }
}

椅子看起来好多了。

我需要提醒两点:

  • 椅子虽然背对着我们,但可以简单地通过沿其 Y 轴旋转进行调整。
  • 为何支架(supports)是黑色,其余是白色?这是因为导入的模型带有材质(material)信息,这是在 Blender 中设置的。但没关系,因为我们将添加一个在程序自定义纹理的函数,支持在椅子模型加载后为不同区域进行设置。如果你有木质和牛仔布纹理(小剧透:本文有),我们就能在加载后立刻进行设置,而无需用户手动选择。因此,椅子目前用什么材质都无关紧要。

跳到 loader 方法,还记得设置缩放比例的地方吗 theModal.scale.set(2,2,2)?我们将调整的旋转角度添加在它下方:

// Set the models initial scale   
  theModel.scale.set(2,2,2);

  theModel.rotation.y = Math.PI;

哇哦,看起来好多了。还有一件事是:据我所知,Three.js 目前仍不支持角度(deg)单位。因此,这里使用 Math.PI,即 180 度,如果想旋转至 45 度角,那么就设置 Math.PI / 4。

我们还需要一块地板,不然怎么产生投影呢?

创建一个平面(二维平面,或高度为 0 的三维体)作为地板。

在光的下方添加:

// Floor
var floorGeometry = new THREE.PlaneGeometry(5000, 5000, 1, 1);
var floorMaterial = new THREE.MeshPhongMaterial({
  color: 0xff0000,
  shininess: 0
});

var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -0.5 * Math.PI;
floor.receiveShadow = true;
floor.position.y = -1;
scene.add(floor);

下面讲解一下这里发生了什么。

首先,我们创建了一个几何图形。这是本文创建的唯一一个几何图形,你可以进行各种参数的调整。

第二,我们使用了 MeshPhongMaterial,为其设置了颜色和反光度(shininess)。在讲 Three.js 其他材质前,我们先看看 Phong。我们能调整它的反光度(reflectiveness)和镜面高光(specular highlights)。另外,还有 MeshStandardMaterial,其支持更多的纹理特性,如金属(metallic)和环境光遮蔽(ambient occlusion);另外,还有不支持阴影的 MeshBasicMaterial。本文仅用到 Phong 材质。

我们创建了变量 floor,并将 geometry 和 material 合为 Mesh。

我们还对地板进行了以下操作:旋转至平坦状态,使其能接收阴影、往下移至椅子的位置。最后将其添加至场景。

现在看起来如下:

我们暂时将地板设为红色,但阴影在哪?为此,我们还需要做几件事。首先在 const renderer 下方添加这几行代码:

// Init the renderer
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});

renderer.shadowMap.enabled = true;
renderer.setPixelRatio(window.devicePixelRatio);

我们同时设置了设备像素比,这与阴影无关,恰巧是适当的位置。我们启用了 shadowMap,但仍没有阴影?

在 loader 函数内,我们能遍历 3D 模型(的组成元素)。因此,跳到 loader 函数,在 theModel = gltf.scene; 下添加以下操作:为 3D 模型的每一个元素(椅腿、坐垫等)启用投射和接收阴影的选项。该遍历方法会在后续再次使用。

theModel = gltf.scene; 下方添加:

theModel.traverse((o) => {
    if (o.isMesh) {
       o.castShadow = true;
       o.receiveShadow = true;
    }
  });

这看起来比以前更糟,但起码能在地板上产生阴影!之所以不好看,是因为模型仍使用 Blender 带来的材质。下面我们将所有这些材质都替换为普通的 PhongMaterial。

在 loader 函数上方创建另一个 PhongMaterial:

// Initial material
const INITIAL_MTL = new THREE.MeshPhongMaterial( { color: 0xf1f1f1, shininess: 10 } );

这是一个不错的起始材质,灰白色和略带光泽。

虽然目前只有一种材质,但为了后续方便为椅子各个部分设置不同颜色或加载的纹理,我们将材质的数据结构声明为一个数组。

// Initial material
const INITIAL_MTL = new THREE.MeshPhongMaterial( { color: 0xf1f1f1, shininess: 10 } );

const INITIAL_MAP = [
  {childID: "back", mtl: INITIAL_MTL},
  {childID: "base", mtl: INITIAL_MTL},
  {childID: "cushions", mtl: INITIAL_MTL},
  {childID: "legs", mtl: INITIAL_MTL},
  {childID: "supports", mtl: INITIAL_MTL},
];

再次遍历 3D 模型(的组成元素),并使用 childID 查找椅子的不同部分,然后设置相应材质(mtl 属性)。模型每个元素的名字都是在 Blender 中设置的,这在第一节讲到。

在 loader 函数下方,添加一个参数为模型、模型的哪部分(type)和材质的函数。我们还为模型的组成元素添加了一个后续会用到的新属性 nameID

// Function - Add the textures to the models
function initColor(parent, type, mtl) {
  parent.traverse((o) => {
    if (o.isMesh) {
      if (o.name.includes(type)) {
        o.material = mtl;
        o.nameID = type; // Set a new property to identify this object
      }
    }
  });
}

在 loader 函数内的 scene.add(theModel) 前遍历 INITIAL_MAP 数组,并执行该函数,:

// Set initial textures
  for (let object of INITIAL_MAP) {
    initColor(theModel, object.childID, object.mtl);
  }

最后,回到地板,将其颜色从红色(0xff0000)改为亮灰色(0xeeeeee)。

// Floor
var floorGeometry = new THREE.PlaneGeometry(5000, 5000, 1, 1);
var floorMaterial = new THREE.MeshPhongMaterial({
  color: 0xeeeeee, // <------- Here
  shininess: 0
});

这里值得注意的是:0xeeeeee 与背景色不同。但在光的作用下,它们看起来一致。

3D Chair Customizer Tutorial - Part 1

恭喜,越来越像样了!如果你卡在某一节点,可以 fork 这个 pen 或参考它,直至找到问题所在。

Part 4:添加控制

实际上,本章节很短,这得益于第三个依赖——OrbitControls.js,让一切变得十分简单。

在声明 animate 函数上方,添加以下控制代码:

// Add controls
var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.maxPolarAngle = Math.PI / 2;
controls.minPolarAngle = Math.PI / 3;
controls.enableDamping = true;
controls.enablePan = false;
controls.dampingFactor = 0.1;
controls.autoRotate = false; // Toggle this if you'd like the chair to automatically rotate
controls.autoRotateSpeed = 0.2; // 30

在 animate 函数内的顶部,添加:

controls.update();

controls 是 OrbitControls 的实例。你可以随意更改其参数,其中包括允许用户旋转椅子(上下)、禁用拖拽以使椅子保持在中心、启用了阻尼使其过渡更自然,还有自动旋转功能(根据个人情况启用与否),但目前是关闭状态。

用你的鼠标或触摸屏进行体验吧!

Scrollable

Part 5:更改颜色

到现在,我们的程序还没进入主题,所以接下来会专注于更改颜色(纹理)。

在 canvas 标签下方添加:

<div class="controls">
<!-- This tray will be filled with colors via JS, and the ability to slide this panel will be added in with a lightweight slider script (no dependency used for this) -->
 <div id="js-tray" class="tray">
     <div id="js-tray-slide" class="tray__slide"></div>
 </div>
</div>

.controls DIV 标签吸附在视口底部,.tray 设为 100%(相对于 body),其子元素 .tray__slide 作为色板,色板可根据需要进行补充。

首先添加几种颜色。在 JavaScript 顶部,添加含有 5 个对象的数组,每个对象都带有 color 属性。

const colors = [
{
    color: '66533C'
},
{
    color: '173A2F'
},
{
    color: '153944'
},
{
    color: '27548D'
},
{
    color: '438AAC'
}  
]
注意:以上十六进制颜色值既没有 #,也没有 0x。这是因为它的使用场景不止一种(CSS 和 Three.js)。另外,之所以使用对象,是因为能添加其他属性,如亮度(shininess)、图片纹理。

使用这些颜色制作色板!

首先在 JavaScript 顶部引用滑块:

const TRAY = document.getElementById('js-tray-slide');

在 JavaScript 底部添加一个名为 buildColors 的函数,并立即调用它。

// Function - Build Colors
function buildColors(colors) {
  for (let [i, color] of colors.entries()) {
    let swatch = document.createElement('div');
    swatch.classList.add('tray__swatch');

    swatch.style.background = "#" + color.color;

    swatch.setAttribute('data-key', i);
    TRAY.append(swatch);
  }
}

buildColors(colors);

上面是我们基于 colors 数组创建的色板列表!注意我们同时为它们设置了 data-key 属性,这是用于查找生成材质的颜色值。

buildColors 函数下方,为色板添加事件处理函数:

// Swatches
const swatches = document.querySelectorAll(".tray__swatch");

for (const swatch of swatches) {
  swatch.addEventListener('click', selectSwatch);
}

点击事件的处理函数命名为 selectSwatch。它会基于色值创建新的 PhongMaterial,并调用另一个函数来遍历 3D 模型(的组成元素),对匹配的部分更换材质!

function selectSwatch(e) {
  let color = colors[parseInt(e.target.dataset.key)];
  let new_mtl;

  new_mtl = new THREE.MeshPhongMaterial({
    color: parseInt('0x' + color.color),
    shininess: color.shininess ? color.shininess : 10
  });
    
  setMaterial(theModel, 'legs', new_mtl);
}

该函数通过 data-key 属性匹配颜色,并基于该颜色创建新材质。

该函数仍不能工作,需要添加 setMaterial 函数。

注意:setMaterial(theModel, 'legs', new_mtl); 第二个参数目前暂且传入 'legs',很快我们就有指定不同部分的能力。目前,首先要实现 setMaterial 函数。

在该函数下方,添加 setMaterial 函数:

function setMaterial(parent, type, mtl) {
  parent.traverse((o) => {
    if (o.isMesh && o.nameID != null) {
      if (o.nameID == type) {
        o.material = mtl;
      }
    }
  });
}

这与 initColor 函数大同小异。nameID 属性来自 initColor,若它与参数 type 相等,就为它添加材质。

现在我们的色板能创建新材质,并更改椅腿的颜色,快来试一试!

Swatches change the legs color!

Part 6:指定哪个部分进行更改

我们已经能更改椅腿的颜色。现在就让我们添加指定更改哪部分颜色的能力。在 body 标签内的顶部添加以下 HTML:

<!-- These toggle the the different parts of the chair that can be edited, note data-option is the key that links to the name of the part in the 3D file -->
<div class="options">
    <div class="option --is-active" data-option="legs">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/legs.svg" alt=""/>
    </div>
    <div class="option" data-option="cushions">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/cushions.svg" alt=""/>
    </div>
    <div class="option" data-option="base">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/base.svg" alt=""/>
    </div>
    <div class="option" data-option="supports">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/supports.svg" alt=""/>
    </div>
    <div class="option" data-option="back">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/back.svg" alt=""/>
    </div>
</div>

这是带有自定义图标的按钮集合。.option DIV 吸附在视口一侧(另外,通过 CSS 的媒介查询还会使其随着视口大小而进行调整)。每个 .option DIV 都是白色正方形,而带有 --is-active 类名的还会有红色边框。另外,还带有用于匹配 nameID data-option 属性。最后,image 元素拥有 pointer-events 属性,即使点击了 image,点击事件的触发始终保留在其父元素。

在 JavaScript 顶部添加另一个变量 activeOptions,其默认值为 legs

var activeOption = 'legs';

回到 selectSwatch 函数,更改硬编码的 legs 参数为 activeOption

setMaterial(theModel, activeOption, new_mtl);

现在我们需要做的是创建事件处理函数,当点击 .option 时更改 activeOption

const swtachesselectSwatch 函数上方添加:

// Select Option
const options = document.querySelectorAll(".option");

for (const option of options) {
  option.addEventListener('click',selectOption);
}

function selectOption(e) {
  let option = e.target;
  activeOption = e.target.dataset.option;
  for (const otherOption of options) {
    otherOption.classList.remove('--is-active');
  }
  option.classList.add('--is-active');
}

该函数会将 event.targetdata-option 值设为 activeOption,并切换 --is-active 类。

体验一下

Changing options

止步于此?物体全是一种材质类型时,难免乏味。下面就增加木和纺织布材质:

const colors = [
{
    texture: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/wood_.jpg',
    size: [2,2,2],
    shininess: 60
},
{
    texture: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/denim_.jpg',
    size: [3, 3, 3],
    shininess: 0
},
{
    color: '66533C'
},
{
    color: '173A2F'
},
{
    color: '153944'
},
{
    color: '27548D'
},
{
    color: '438AAC'
}  
]

前两个是纹理,分别是木和牛仔布。另外,还增加了两个新属性——sizeshininesssize 表示重复图案的频率,所以数值越大图案越密集。

现在我们要更新两个函数以支持该特性。首先将 buildColors 更新为:

// Function - Build Colors

function buildColors(colors) {
  for (let [i, color] of colors.entries()) {
    let swatch = document.createElement('div');
    swatch.classList.add('tray__swatch');
    
    if (color.texture)
    {
      swatch.style.backgroundImage = "url(" + color.texture + ")";   
    } else
    {
      swatch.style.background = "#" + color.color;
    }

    swatch.setAttribute('data-key', i);
    TRAY.append(swatch);
  }
}

现在它会检查是否存在 texture 属性,若存在,则将色板的背景设为该纹理。

注意到第 5、6 块色板之间的间距了吗?我通过 CSS 将每 5 个色板作为一组,这对于拥有更多色板数量时显得尤为重要。

第二个需要更新的函数是 selectSwatch

function selectSwatch(e) {
  let color = colors[parseInt(e.target.dataset.key)];
  let new_mtl;

  if (color.texture) {
    let txt = new THREE.TextureLoader().load(color.texture);

    txt.repeat.set( color.size[0], color.size[1], color.size[2]);
    txt.wrapS = THREE.RepeatWrapping;
    txt.wrapT = THREE.RepeatWrapping;
      
    new_mtl = new THREE.MeshPhongMaterial( {
      map: txt,
      shininess: color.shininess ? color.shininess : 10
    });    
  } 
  else {
    new_mtl = new THREE.MeshPhongMaterial({
      color: parseInt('0x' + color.color),
      shininess: color.shininess ? color.shininess : 10
    });
  }
    
  setMaterial(theModel, activeOption, new_mtl);
}

该函数会检查当前色板是不是纹理,若是,则通过 Three.js 的 TextureLoader 方法创建新纹理,并将该纹理的 repeat 设为色板 size 值。另外,还为纹理设置 wrapping(经试验后得出效果最佳的 wrapping 值),然后为 PhongMaterial 的 map 属性设置为当前纹理,最后设置 shininess 值。

如果当前色板无 texture 属性,则使用老方法。请注意,你也可以为了纯色的材质设置 shininess 值。

重要:如果添加纹理后椅子表现为黑色,请查看 console,判断是否是跨域导致的问题?这也是 CodePen 的问题,建议注册 Cloudinary 并使用其免费套餐存放图片。

这里带有纹理的 pen:

Texture support

Part 7:收尾工作

我曾有个项目交付给客户验收,这个项目有一个大按钮去祈求被按,甚至在 hover 时闪闪发光,然而客户及其同事(会计部的 Dave)却反馈他们不知道要按什么(去你的,Dave)。

在 canvas 标签上方添加一些号召性语句:

<!-- Just a quick notice to the user that it can be interacted with -->
<span class="drag-notice" id="js-drag-notice">Drag to rotate 360&#176;</span>

通过 CSS 将其放在椅子前方,用于指示用户可拖拽旋转椅子。但椅子仅仅呆滞不动?

让椅子在首次加载后进行旋转,旋转完毕后隐藏引导语。

首先在 JavaScript 上方添加 loaded 变量,并设为 false

var loaded = false;

在 JavaScript 底部添加该函数:

// Function - Opening rotate
let initRotate = 0;

function initialRotation() {
  initRotate++;
  if (initRotate <= 120) {
    theModel.rotation.y += Math.PI / 60;
  } else {
    loaded = true;
  }
}

模型需要在 120 帧内线性旋转 360 度(约 2 秒,60fps),所以我们将在 animate 函数中运行该函数 120 次,一旦完成则将 loaded 设为 true。代码如下:

function animate() {

  controls.update();
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
  
  if (resizeRendererToDisplaySize(renderer)) {
    const canvas = renderer.domElement;
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();
  }
  
  if (theModel != null && loaded == false) {
    initialRotation();
  }
}

animate();

我们判断 theModel 是否不等于 nullloaded 是否为 false,若符合条件,则调用该函数 120 次,然后将 loaded 设为 true,使得 animate 函数最终忽略它。

这就拥有了自旋转的椅子。椅子停止的这一刻是删除引导语的好时机。

在 CSS 中,为引导语添加一个带有隐藏动画的类,该动画的延迟时间为 3 秒,所以,在开始旋转椅子的同时为引导语添加该类。

在 JavaScript 顶部引用引导语:

const DRAG_NOTICE = document.getElementById('js-drag-notice');

更新 animate 函数:

if (theModel != null && loaded == false) {
    initialRotation();
    DRAG_NOTICE.classList.add('start');
  }

好极了!这里有更丰富的颜色供你选择。同时,下方也提供了轻量无依赖的滑动功能(用于拖拽滑动色板列表):

const colors = [
{
    texture: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/wood_.jpg',
    size: [2,2,2],
    shininess: 60
},
{
    texture: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/fabric_.jpg',
    size: [4, 4, 4],
    shininess: 0
},
{
    texture: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/pattern_.jpg',
    size: [8, 8, 8],
    shininess: 10
},
{
    texture: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/denim_.jpg',
    size: [3, 3, 3],
    shininess: 0
},
{
    texture: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/1376484/quilt_.jpg',
    size: [6, 6, 6],
    shininess: 0
},
{
    color: '131417'  
},
{
    color: '374047'  
},
{
    color: '5f6e78'  
},
{
    color: '7f8a93'  
},
{
    color: '97a1a7'  
},
{
    color: 'acb4b9'  
},
{
    color: 'DF9998',
},
{
    color: '7C6862'
},
{
    color: 'A3AB84'
},
{
    color: 'D6CCB1'
},
{
    color: 'F8D5C4'
},
{
    color: 'A3AE99'
},
{
    color: 'EFF2F2'
},
{
    color: 'B0C5C1'
},
{
    color: '8B8C8C'
},
{
    color: '565F59'
},
{
    color: 'CB304A'
},
{
    color: 'FED7C8'
},
{
    color: 'C7BDBD'
},
{
    color: '3DCBBE'
},
{
    color: '264B4F'
},
{
    color: '389389'
},
{
    color: '85BEAE'
},
{
    color: 'F2DABA'
},
{
    color: 'F2A97F'
},
{
    color: 'D85F52'
},
{
    color: 'D92E37'
},
{
    color: 'FC9736'
},
{
    color: 'F7BD69'
},
{
    color: 'A4D09C'
},
{
    color: '4C8A67'
},
{
    color: '25608A'
},
{
    color: '75C8C6'
},
{
    color: 'F5E4B7'
},
{
    color: 'E69041'
},
{
    color: 'E56013'
},
{
    color: '11101D'
},
{
    color: '630609'
},
{
    color: 'C9240E'
},
{
    color: 'EC4B17'
},
{
    color: '281A1C'
},
{
    color: '4F556F'
},
{
    color: '64739B'
},
{
    color: 'CDBAC7'
},
{
    color: '946F43'
},
{
    color: '66533C'
},
{
    color: '173A2F'
},
{
    color: '153944'
},
{
    color: '27548D'
},
{
    color: '438AAC'
}
]

在 JavaScript 底部添加 slider 函数,它将使你拥有可通过鼠标或触摸屏拖拽色板的能力。为了紧扣主题,这里就不过多研究其工作原理。

var slider = document.getElementById('js-tray'), sliderItems = document.getElementById('js-tray-slide'), difference;

function slide(wrapper, items) {
  var posX1 = 0,
      posX2 = 0,
      posInitial,
      threshold = 20,
      posFinal,
      slides = items.getElementsByClassName('tray__swatch');
  
  // Mouse events
  items.onmousedown = dragStart;
  
  // Touch events
  items.addEventListener('touchstart', dragStart);
  items.addEventListener('touchend', dragEnd);
  items.addEventListener('touchmove', dragAction);


  function dragStart (e) {
    e = e || window.event;
     posInitial = items.offsetLeft;
     difference = sliderItems.offsetWidth - slider.offsetWidth;
     difference = difference * -1;
    
    if (e.type == 'touchstart') {
      posX1 = e.touches[0].clientX;
    } else {
      posX1 = e.clientX;
      document.onmouseup = dragEnd;
      document.onmousemove = dragAction;
    }
  }

  function dragAction (e) {
    e = e || window.event;
    
    if (e.type == 'touchmove') {
      posX2 = posX1 - e.touches[0].clientX;
      posX1 = e.touches[0].clientX;
    } else {
      posX2 = posX1 - e.clientX;
      posX1 = e.clientX;
    }
    
    if (items.offsetLeft - posX2 <= 0 && items.offsetLeft - posX2 >= difference) {
        items.style.left = (items.offsetLeft - posX2) + "px";
    }
  }
  
  function dragEnd (e) {
    posFinal = items.offsetLeft;
    if (posFinal - posInitial < -threshold) { } else if (posFinal - posInitial > threshold) {

    } else {
      items.style.left = (posInitial) + "px";
    }

    document.onmouseup = null;
    document.onmousemove = null;
  }

}

slide(slider, sliderItems);

现在,将 CSS 内的 .tray__slider 小动画注释掉:

/*   transform: translateX(-50%);
  animation: wheelin 1s 2s ease-in-out forwards; */

剩下最后两步收尾工作就完成了!

更新 .controls div,让其包含引导语:

<div class="controls">
<div class="info">
    <div class="info__message">
        <p><strong>&nbsp;Grab&nbsp;</strong> to rotate chair. <strong>&nbsp;Scroll&nbsp;</strong> to zoom. <strong>&nbsp;Drag&nbsp;</strong> swatches to view more.</p>
    </div>
</div>

<!-- This tray will be filled with colors via JS, and the ability to slide this panel will be added in with a lightweight slider script (no dependency used for this) -->
 <div id="js-tray" class="tray">
     <div id="js-tray-slide" class="tray__slide"></div>
 </div>
</div>

现在我们拥有了一个新的信息块,其包含描述如何控制应用的一些说明。

最后,增加一个 loading 遮罩层,以确保在应用加载期间页面是干净的,并在模型加载后将其删除。

在 body 内的顶部增加以下 HTML。

<!-- The loading element overlays all else until the model is loaded, at which point we remove this element from the DOM -->  
<div class="loading" id="js-loader"><div class="loader"></div></div>

为了使其优先加载,我们将这些 CSS 单独放在 head 标签内,而不是链接式的 CSS 中。所以,在 head 闭合标签上方添加以下 CSS。

.loading {
  position: fixed;
  z-index: 50;
  width: 100%;
  height: 100%;
  top: 0; left: 0;
  background: #f1f1f1;
  display: flex;
  justify-content: center;
  align-items: center;
}

.loader{
  -webkit-perspective: 120px;
  -moz-perspective: 120px;
  -ms-perspective: 120px;
  perspective: 120px;
  width: 100px;
  height: 100px;
}

.loader:before{
  content: "";
  position: absolute;
  left: 25px;
  top: 25px;
  width: 50px;
  height: 50px;
  background-color: #ff0000;
  animation: flip 1s infinite;
}

@keyframes flip {
  0% {
    transform: rotate(0);
  }

  50% {
    transform: rotateY(180deg);
  }

  100% {
    transform: rotateY(180deg)  rotateX(180deg);
  }
}

快好了!在加载模型后将其删除。

在 JavaScript 顶部引用它:

const LOADER = document.getElementById('js-loader');

loader 函数中,在 scene.add(theModel) 后放置以下代码:

// Remove the loader
  LOADER.remove();

现在,模型会在该 DIV 背后加载:

就这样!以下就是完整的 pen,仅供参考!

See the Pen 3D Chair Customizer Tutorial - Part 4 by Kyle Wetton (@kylewetton) on CodePen.

你还可以体验托管在 Codrops 上的 案例

感谢您的支持!

这是一篇长篇教程。如果你发现错误,请在评论告诉我。

四、女装3d激情

作为一个时尚博主,我经常关注女性时装潮流趋势,而今天我想要分享的是绝对让你惊艳的女装3D激情系列。

什么是女装3D激情?

女装3D激情,是近年来兴起的一种时尚潮流,强调了立体感与视觉冲击力。3D效果的设计元素在女装领域中的应用越来越受到设计师和时尚爱好者们的青睐。

3D激情的时装设计

女装3D激情系列在设计上有着独特的创意和非凡的视觉效果。设计师们运用了各种技巧和材质,通过层层叠叠的纹理、立体剪裁和物块拼贴等方式,创造出了一种立体感极强的时尚风格。

这些设计作品不仅仅是服装,更是一种艺术品。每一件女装3D激情系列的设计作品都蕴含着设计师们的心血和创意。独特的剪裁和构造,使得穿上这样的衣服的女性们能够立刻成为众人注目的焦点。

女装3D激情系列的魅力

女装3D激情系列的魅力不仅体现在设计上,更体现在穿着上。这些时尚设计作品能够营造出与众不同的形象和风格,让女性们自信十足地展现自己的个性和魅力。

穿上女装3D激情系列的衣服,你会感受到立体设计带来的视觉冲击,仿佛身处一个全新的时尚世界。这种穿着方式不仅能够让你与众不同,还能够增添一份神秘和诱惑。

如何搭配女装3D激情

搭配女装3D激情系列的衣服需要一定的技巧和搭配意识。首先,要注意衣服的颜色和纹理,选择与3D设计相呼应的配饰和鞋子。

其次,要注意整体造型的平衡感。女装3D激情系列的衣服通常具有较大的视觉冲击力,因此应该搭配简洁、低调的其他服饰,避免造成过于花哨和杂乱的视觉效果。

最后,要注意气质和自信。女装3D激情系列的衣服需要自信和大胆的女性来展现,只有拥有自信的气质才能将这种时尚风格诠释得完美。

女装3D激情行业的趋势和前景

女装3D激情系列正逐渐在时尚界崭露头角,并吸引了越来越多的关注和热爱者。许多知名时尚品牌和设计师也开始将3D元素应用到自己的设计中,为这个行业带来了新的活力和创意。

未来,女装3D激情系列有着广阔的发展前景。随着科技的不断进步和时尚潮流的不断变化,我们可以期待更多创新和突破的时装设计作品出现。女性们也将有更多机会通过穿上3D激情的时装来展示自己的个性与风格。

总结

女装3D激情系列以其独特的设计和视觉效果,在时尚界引起了巨大的影响力。这种独特的时装风格能够让女性们展现自己的个性和魅力,成为众人注目的焦点。

无论是设计师还是消费者,女装3D激情系列都代表了时尚的进步和创新。让我们拭目以待,期待更多令人惊艳的女装3D激情时装设计作品的面世。

五、three d是什么牌子服装?

threed(3d)女装品牌简介

Threed问世于2006年夏、以自然清新为主轴,带有优雅复古的元素,对于素材、质感、剪裁、细节所有坚持的设计品牌,Extra define in natural (自然的额外定义),是Three d主要的品牌形象。主要针对25-35岁有自我形象、自我生活态度、独立而有影响力的时代女性。尊重个体的性格所带来的那些小性感,小可爱,以及女性的魅力,通过 Three d变得更有味道。

六、裸眼 3D 是否需要 3D 片源?

那么什么是真正意义上的裸眼3D呢?

所谓裸眼3D基本上都是针对双目视差来说的。

什么是双目视差:人有两只

眼睛

,它们之间大约相隔65mm。当我们观看一个物体,两眼视轴辐合在这个物体上时,物体的映像将落在两眼网膜的对应点上。这时如果将两眼网膜重叠起来,它们的视像应该重合在一起,即看到单一、清晰的物体。根据这一事实,当两眼辐合到空间中的一点时,我们可以确定一个假想的平面,这个平面上的所有各点都将刺激两眼网膜的对应区域。这个表面就叫做视觉单像区(horopter)。它可以定义为在一定的辐合条件下,在视网膜对应区域的成像空间中所有各点的轨迹。位于视觉单像区的物体,都将落在视网膜对应点而形成单个的映像。

如果两眼成像的网膜部位相差太大,那么人们看到的将是双像,即把同一个物体看成两个。例如,我们用右手举起一支铅笔,让它和远处墙角的直线平行。这时如果我们注视远处墙角的直线,那么近处的铅笔就将出现双像;如果我们注视近处的铅笔,远处的墙角直线就将出现双像。

正因为双目视差,才会让我们看到的物体有纵深感和空间感。

裸眼3D是怎么做到蒙骗双眼来营造空间和纵深感呢,现在的3D视频或者图像都是通过区分左右眼来拍摄的两幅图,视差距约为65mm,通过让你左眼看到左眼的图像,右眼看到右眼的图像就可以让你的大脑合成一副有纵深感的立体画面。

使用屏幕怎么做到呢,目前主流的技术分两种:

1.光屏障式技术

光屏障式3D技术的实现方法是使用一个开关液晶屏、偏振膜和高分子液晶层,利用液晶层和偏振膜制造出一系列方向为90°的垂直条纹。这些条纹宽几十微米,通过它们的光就形成了垂直的细条栅模式,称之为“视差障壁”。而该技术正是利用了安置在背光模块及LCD面板间的视差障壁。通过将左眼和右眼的可视画面分开,使观者看到3D影像。

原理如下:

这种技术的优点是在成本上比较有优势,像夏普的3D手机和

任天堂

3DS

游戏机都是采用这种技术。不过采用这种技术的屏幕亮度偏低,只能在一个合适的角度和位置才能有比较好的视觉效果。

2.柱状透镜技术

柱状透镜技术也被称为微柱透镜3D技术,使液晶屏的像平面位于透镜的焦平面上,这样在每个柱透镜下面的图像的像素被分成几个子像素,这样透镜就能以不同的方向投影每个子像素。于是双眼从不同的角度观看显示屏,就看到不同的子像素。

柱状透镜技术并不会像光屏障式那样影响屏幕亮度,所以其比后者的显示效果要好。

但是这两种技术都存在视点固定,视角也固定,观看3D效果的时候既不能移动也不能晃动,

原因在于他们的3D区域是固定的,一点震动或者移动都会影响观看效果,比如3DS这货,你玩游戏的时候会抖动,那么使用起来就会晕。

而且这种技术都是单人观看的,那么电视是多人观看的,怎么办呢?

这就要说说霜叶提到的TCL的裸眼3D电视,相信很多人在深圳老机场的安检通道的时候看过TCL的那几个裸眼3D电视吧,那就是我在TCL那段时间搞的,那玩意基本上家用不可能的,当时花了大几十万吧,另外普通的3D片源是不能看的,为什么这么说呢?我们来看看他的成像原理。

单人观看的裸眼3D上面都介绍了,毕竟都是单视点的,两幅图就能分成一个3D观看区,那么多几幅图不就有多个3D观看区了吗?可以肯定的是这是他妈真的,有个公司提供了这个技术方案,一张图片分成6个左右分区(12个图)那么理论上就有6个比较舒服的3D观看区了,嗯,TCL那个电视就是这个原理。

贴张模拟图你们感受下。

裸眼3D或者3D都是靠通过左右眼来欺骗大脑对于纵深和空间的感觉,通常意义上3D必须是左右眼的两幅画面合成,如果不是3D片源是不能达到目的。

七、three d v是什么牌子服装?

无法确定因为"three d v"不是一种已知的常见服装品牌,可能只是某个品牌的缩写或者独立设计师的品牌。需要更多的信息才能确定其具体含义和品牌身份。如果您能提供更多的相关信息,如品牌国家、产品类型等,我们可以更准确地回答您的问题。

八、没有3D眼镜,可以看3D电影吗?

感觉戴着3D眼镜特别累,每次去电影院看3D电影,都感觉眼睛好累啊。朋友跟我说有裸眼3D设备,叫超多维3D BOX,也不知道真假,总觉得这种高科技的东西都不怎么实用,有没有人用过?好用吗?确实是不戴眼镜就可以看3D电影吗?

九、如果古董可以3D扫描和3D打印?

今天的技术,已经可以3d扫描+3d打印一条龙了。

http://haokan.baidu.com/v?pd=wisenatural&vid=2945196082714108420

上次在有图不看到有个视频,一个博物馆展出了可以触摸的名画。是《戴珍珠耳环的少女》。

图片来自百度,侵删。

当然不可能让原画拿出来给你摸。

是三维扫描后再打印出来的复制品,完美复刻了上面沟沟壑壑的笔触纹路,可以让艺术爱好者上下其手。爽不爽?

这技术,3d扫描朱叫兽可以做,我们的vr博物馆里就有一张油画。

有vr设备的可以点这里:

https://www.viveport.com/apps/c468b843-fe23-47ca-946c-68cee7a22bf7?from=timeline

没有vr设备的可以看这里:

https://m.youku.com/video/id_XMzY0NzUyOTA0OA==.html?source=&ishttps=1&sharetype=2&from=timeline

打印的话,比较贵。只有荷兰那边有这种油画打印机。

其他文物古董的话,我们的3d扫描也都超级真实地还原了真品的细节。

瓷器花瓶:

http://wusuobuwei.com/caseshow.php?id=180

明孝陵里的石麒麟:

http://www.wusuobuwei.com/caseshow.php?id=136

瑞典大师手工打造的茶具:

http://wusuobuwei.com/caseshow.php?id=209

扫描这边是没有问题了,只剩下打印技术,不给力啊。。。

我们是另存维信息科技,不赶紧把你的宝贝另存一下吗?

十、IMAX 3D 比普通 3D 效果好多少?

14.07.15更新国内可以放映70毫米胶片IMAX的影院。

15.01.10~16.05.17各种小修小改;19.02.27补 [阿丽塔:战斗天使] 的杜比体验。

我想补充一下个人在多次观影后得出的观点:其实某些情况下IMAX + 3D并不是无敌的,好的realD设备和杜比视界观影体验就会比IMAX好。制作精良、充分考虑IMAX特性的电影,才能达到IMAX 3D效果完胜普通3D的程度。

目前普通3D放映格式和品牌主要有Xpand,杜比,master image,Leonis(雷欧尼斯),realD,魔影,索尼4K等等。Xpand和杜比①属于主动式3D,效果都很差已经逐渐淡出市场,双机和4K影厅目前不多我没有体验过。魔影、master image、Leonis的成像原理都是类似realD的偏振式3D,按理说相比主动式3D,亮度损失最小且眼镜轻巧(仅看过一次巨幕魔影3D的变4,出屏不错但亮度略暗;最近又看了双机Leonis放映机的[一代宗师3D],亮度没问题立体感竟非常差,可见 亮度达标也不是3D体验好的保证。另外看过多次不知道什么品牌的其它偏振式3D,亮度和立体效果都远不如realD)。由于realD品控较好普及较广,可以具体查询到使用realD设备的影厅,一会我会主要用realD3D作为普通3D的代表来和IMAX 3D做比较。

和2D相比,3D景深要有突出的主体以造成空间错落感,观看3D画面人眼注意力更集中于画面中心,会比2D版更不易顾全整体画面和画面细节(特别是背景)。3D电影不能有太多景深变化较大的镜头切换,否则很快会让人眼疲劳(如[少年派的奇幻漂流]和[了不起的盖茨比]在切换一些景深差距较大的镜头时会使用渐变重叠过渡来衔接)。因此好的3D电影不只是要立体感明显画面明亮,还要能适应人眼的成像习惯,否则让人看一会就眼部疲劳,再好的出屏效果也是枉然。

IMAX特性是屏幕大,视野超宽广,在远景大场面和长镜头上的临场感是无与伦比的。也因为大,IMAX的弊端就是不适合过多表现特写近景,不稳定的晃动镜头。镜头里的高楼出现在几十米高的银幕上会很爽,但要是一张张大脸充满了几十米高的银幕,相信不会让观众产生更多的愉悦感,反而可能会带来心理上的不适。况且银幕越大,大脑处理新画面的信息就会越费时,因此不稳定的晃动镜头和快速切换镜头会给观众带来比普通银幕更强烈的不适感,很容易导致还没看清楚这个画面就切到下个画面的情况,我个人的感受是同一部影片看IMAX版观看时更难消化剧情导致错过关键信息,普通银幕就要好很多。

再加上3D,IMAX 3D更要注意合理构图,一个场景背景信息不能塞太多,两个连接的镜头景深不能变化过大,全片特写和近景不能多,否则3D弊端带来的负面效果只会在IMAX银幕上被加倍放大。

设备:

IMAX使用的3D设备是双机线偏振放映系统②,虽然亮度和效果有保障,比起realD的圆偏振仍有先天缺陷,如头部稍微倾斜一定角度画面就不立体,连续运动画面会有重影产生等。

IMAX的音响设备是5.1声道(一说是五个分立的音频通道,与传统影院的5.1不同;一说是六声道超级音响系统),主打“撼天动地”的震撼。而已经有一些院线配备了realD或巨幕 + 杜比全景声,加上现在杜比全景声制作的影片越来越多,IMAX的音效优势也不再明显。

片源:

数字IMAX的片源一般有两种画幅,暂且不论是后期DMR转制还是原生IMAX摄像机拍摄,仅在放映效果上大致表现为满屏(16:9,1.85:1,或1.9:1)的和上下有两条黑边的超宽屏(2.35:1或近似)两种。由于3D有明显焦点的特质显然更适合接近方形的画幅比例。而IMAX本身银幕尺寸巨大,上下两条黑边也很影响观感,更适合偏方形画面。这就是为什么数字IMAX依然默认满屏1.85:1,而胶片IMAX满屏的1.44:1更是震撼无比(有机会请一定去北京科技馆看看真正的胶片巨幕科教片3D,出屏效果和满屏带来视觉上的愉悦感完爆几乎所有商业片)。

由此看詹姆斯·卡梅隆、克里斯托弗·诺兰等人真是业界良心。[阿凡达]是满屏,[泰坦尼克号]原本是2.35:1在转3D版后居然扩充成的16:9来充分适应3D和IMAX。诺兰的所有IMAX电影坚持外景用胶片IMAX拍摄,所有IMAX满屏段落即使是在清晰度只有2k的数字IMAX上都是IMAX体验的极致([星际穿越]除外)。另外顺便提一下约瑟夫·科金斯基,他的[创:战记]和[遗落战境]口碑毁誉参半,但他在3D和IMAX视效方面也有深刻的理解,[创:战纪]也使用IMAX摄像机拍摄游戏段落,3D效果相当出色;[遗落战境]在IMAX 2D上全程满屏,全片大部分都是长镜头和远景俯拍相当过瘾。

正如张小北所说,以[阿凡达]、[雨果]、[少年派]和[地心引力]为代表的很少的几部电影在IMAX 3D的体验是无与伦比的,它们充分发挥了3D的优势并回避了的3D的短板,这些优势在IMAX银幕上被加倍放大。但剩下大多数的IMAX电影,很难说能发挥出这块大银幕的长处。

一般影片,IMAX 2D比普通2D更佳。除了巨幕带来的震撼,比起3D的观感人眼也更舒服。

如蝙蝠侠黑暗骑士系列,[安德的游戏]等。尤其是宇宙和科幻题材,一段段长镜头都能爽到飞。也有部分坑爹转制IMAX滥竽充数的影片,毫不考虑巨幕下人眼适应画面的特点,手提摄影能把观众晃到吐,如[饥饿游戏]和[极乐空间],后来部分段落用真胶片IMAX机拍摄的影片[饥饿游戏:燃烧的女孩]实际效果也并不理想。

3D影片。2.35:1画幅的、有杜比全景声的近景和大特写镜头多、手提摄影风格明显快速剪切多的,具备以上三点中任意两点或一点或三点的请果断放弃IMAX而选择realD+杜比全景声。

反面典型:[星际迷航:暗黑无界]不停的晃动+眩光,用IMAX摄像机拍摄的满屏场景基本全是近景和CGI,无异于脱裤放屁。还有漫威旗下的所有电影,一水的IMAX 3D,一水的劣质伪3D和2.35:1,甚至杜比全景声也做的不好。

再以彼得·杰克逊为例。他本身推崇realD的降噪优化画面的系统,使用真3D设备拍摄霍比特人系列。我先后观看了[霍比特人:史矛革之战]的IMAX版和realD版,realD在颜色的饱和度和鲜艳度上明显好于IMAX。在观感上以精灵兽人矮人木桶漂流大战的段落为例,虽然IMAX上产生了短暂的“坐过山车感”,但这一整段上由于镜头剪切较多,主体在画面中不停的运动,看得并不是很爽,全片看下来也很累。后来因为陪弟弟去看了realD,顿觉漂流那一段极其入戏一气呵成,全片也没之前感觉那么无趣了。也是从这次开始我渐渐的改变了IMAX至尊的观点。

顺便说一下大家都很称赞的[环太平洋],巨大机甲和怪兽在IMAX上的表现确实很震撼,但你有没有觉得一但他们打起来根本看不清敌我双方的和具体动作细节?甚至全片下来几代怪兽的款式区别也不是很明显?个人认为就是因为[环太平洋]的各种特写较多,以近景为主,即使在IMAX影厅的皇帝坐观看也无暇顾及全景或细节。也恰好我后来在亮度尚可的杜比3D影厅重看环太,获得了比之前更好的观影体验。

动画片方面在IMAX上看过[神偷奶爸/卑鄙的我2],3D出屏确实好到爆,而且全片一直有各种出屏场景,不停的提醒你这是一部3D电影,然后我看到一半就不得不摘下眼镜缓解眼睛疼痛了...更不用说全场那么多小孩,到后面有几个小孩都看不下去了。顺便[一步之遥]也可以归到这类反面典型里。

还有16年的[美国队长3],作为第一部使用IMAX 2D数字摄影机(ALEXA 65)拍摄的转3D影片,画面晃动和快速剪切居然比[美国队长2]还狠,虽然有17分钟的1.9:1的IMAX满屏画面,但是…单单一幕突然插进来的蚁人微观世界仅几秒又切到其他视角那猝不及防的3D景深突变,就够瞎一会眼了,普通厅看下来很累,IMAX因为景深感不如realD反而“因祸得福”的没有那么明显的不适,但也没有提升太多的体验。

其实说了这么多就是想说,无需迷信IMAX,理性挑选观影环境

当然也要意识到,如今IMAX大行其道,和IMAX公司严格的品控和高逼格宣传是分不开的。只要有着IMAX的Logo,就意味着覆盖全部视野的画面,舒适轻巧的眼镜,高亮3D③,合理的座位坡度,撼天动地的音效和经过DMR转制的片源.....(美中不足的是IMAX公司似乎没有措施来保证座椅的舒适度)而完全不用操心去普通影院看电影影厅的设备是主动还是被动,亮度是否合适,音响是不是杜比,大厅还是小厅之类的一串问题。所以IMAX意味着用多几十块的票价换来更高的性价比和更省心的观影选择(听着似乎有点像ios和安卓)。即使是第一次带同学去看IMAX的[星际迷航:暗黑无解],[超人:钢铁之躯]和[饥饿游戏2:星火燎原]这样的影片,他们均表示有了再也不想看非IMAX电影的感觉。而且!遇到[阿凡达]、[少年派]、[地心引力]这样的电影你不去IMAX至少刷一遍真的是你吃亏好嘛!

希望将来真正适应IMAX 3D格式的大片会越来越多,激光4k的数字IMAX机的普及不要等太久,以及杜比全景声可以早日与IMAX合璧。最大的心愿还是,全国那三块(此处错误,应该有十块,详情在文末)业界良心的胶片IMAX不要改造成数字,即使是以后再也没有商业IMAX片用胶片机拍,看科教片也愿意④。

--------------------------------------------------

不是影视从业者,也没有做过专门的调查,以上结论都是以我的观感为主而得出的,也要感谢微博上的几位大大无私为我提供相关知识。以上若有任何问题和错误请务必提出,我虚心接受,也请勿人身攻击恶意反对。

--------------------------------------------------

14.5.6更新:把评论里的几个问题和回复贴在这方便大家看看

4K厅放的一定是4K分辨率的电影吗?如何分辨?realD呢?

@张子谦:目前4K拷贝很少,所以大多数4K厅还是放的2K拷贝。银幕不大的情况下区别不是很明显。拿到的眼镜上有realD的logo的话就是realD的了。

楼上回答已经很全面了。在影院不公开说明某片片源是4k还是2k的情况,只能靠多看几次积攒的经验来分辨清晰度了(我觉得4k和2k差别应该是肉眼明显可辨...唯一一次观看双机4k 2D的电影却有重影...

哪里能查到X地的realD的影院/影厅?

去realD官方微博的《全国城市RealD影院列表》查。找到你想去的电影院后,记得接着直接问博主该影院几号厅使用了realD设备。

RealD_3D的微博

以我在的西安为例,西安万达影城曾经解放路店为4/5/8影厅,民乐园店为4/5号厅。不过后来去民乐园店5号厅先后观看[疯狂原始人]和[了不起的盖茨比],眼镜换了两幅都又油又脏,只好用自己带的眼镜。画面失焦,字幕都是模糊的,光线暗,大白天的阳光跟黄昏似的。然后去4号厅看[怪兽大学],3D居然是Xpand的主动式= =

文中的皇帝座怎么算的

不同大小的IMAX厅具体的最佳排数还是有区别的。我自己的经验是多次观看以后,屏幕能正好充满全部视野的那排再往后推个1~2排的最中间座位。因为屏幕能正好充满全部视野的那排看原声电影的字幕很累...一般国语配音或非3D满屏的IMAX我才会坐这一排。不过抛开其他因素,IMAX3D越靠前越震撼,二刷少年派坐到了第四排,完全入画了

不过第四排也算是往前的极限,再往前画面扭曲的就无法直视了...当初在第一排看泰坦尼克号永生难忘

16:9的比例算是满屏吗?

由于以前疏忽误以为16:9在数字IMAX上是满屏。

此处更正:不算。数字IMAX银幕比例为1.85:1,而非16:9或1.78:1。但部分画幅是16:9的影片如[阿凡达]、[泰坦尼克号3D]、[少年派的奇幻漂流]、[环太平洋]等,在数字IMAX上的表现几乎为满屏,左右会有黑边。

看3d电影怎么分辨看的是什么格式的?

一般3D眼镜厚重体积大的是主动式,内置电池,观影效果差佩戴易不适。体积小轻巧塑料镜架塑料片镜片的是被动式,reald和IMAX的3D都在此类。然后眼镜架上印realD的logo的话肯定就是realD的

14.7.15更新:

更正。国内可以放映70毫米胶片IMAX的地方有10家。

分别是:中国科技馆,电影博物馆,广东科学中心,上海科技馆,重庆少年宫,大庆科技馆,东莞科技馆,黑龙江科技馆,香港太空馆,南京青少年科技中心。

但诺兰的[星际穿越](Interstellar)胶片版IMAX由于内地压根没有引进所以不会这十块银幕上上映。感谢新浪微博的@浮夸的拉尔夫 提供信息。原博:Sina Visitor System

14.11.23更正:

数字IMAX满屏画幅是1.85:1,而非我之前所说的16:9或1.78:1。在此向被误导的各位表示抱歉。

你们感兴趣的话可以去反老师微博搜搜“IMAX”“3D”等关键词,非常长姿势。

14.11.25 发一张最近无聊时用手机做的效果图:

完全是用手机系统自带app做的!花了半个多小时呢快来夸我!~\(≧▽≦)/~

16.05.09 两年后根据实际情况对几处内容加上注释

①Xpand和杜比属于主动式3D:这里的杜比效果体验不包括杜比公司最新的杜比Vision技术和激光IMAX 3D的杜比技术。

理论上杜比影院(杜比视界+杜比全景声+定制装修)效果算是无敌的存在,双机+4K+HDR,业界最强对比度+支持120帧,业界2D最高亮度,3D仅激光IMAX亮度可以追平,高落差排距+可调节躺式无敌沙发,可以说除了3D眼睛漏光的缺陷 和 少量电影不会有IMAX独家满屏的画面,综合体验基本完胜,看2D基本无敌。我先后看了数字IMAX 3D和杜比影院2D的 [阿丽塔:战斗天使] 。杜比的清晰度和色彩鲜艳度,黑色的深度都要好于IMAX 3D太多,至于前者的满屏内容,我在看电影前没有了解到有特殊画幅,看电影时也居然完全没注意到,就很......(延伸话题:IMAX独家满屏究竟是不是歪风邪气?对于电影的实际体验能有多少提升?而普通银幕凭什么就要看被裁切的画面?导演在考虑构图时究竟该以什么版本为基准?[银翼杀手2049] 搞出了独家满屏,结果摄影指导却说宽画幅才是我们想要的效果,所以独家画幅在这里反而是开倒车?)

②IMAX使用的3D设备是线偏振放映系统:这里特指我们身边普及较广的普通数字IMAX。激光IMAX为圆偏振3D。

③高亮3D:近年来IMAX银幕的亮度难以保障,经常在社交网络看到关于某块新开的IMAX亮度要好于其他某块。我常去的运营了4年的IMAX影院亮度堪忧,[蝙蝠侠大战超人:正义黎明]开头玛莎·韦恩瞳孔放大的镜头看不清瞳孔瞳孔和眼珠的界限,IMAX官网和官微也不接受投诉。

④即使是以后再也没有商业IMAX片用胶片机拍,看科教片也愿意:IMAX胶片设备进行数字IMAX激光改造将是今后的趋势,同样支持放映胶片IMAX摄影机所拍摄的1.44:1画幅影片,清晰度达到4k。12声道。目前国内有东莞万达电影城,电影博物馆也将进行激光改造。(不够大的普通IMAX厅就别想着会被改造了XD

顶一下
(0)
0%
踩一下
(0)
0%
相关评论
我要评论
点击我更换图片