惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
T
The Blog of Author Tim Ferriss
The GitHub Blog
The GitHub Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
A
About on SuperTechFans
Stack Overflow Blog
Stack Overflow Blog
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
腾讯CDC
爱范儿
爱范儿
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 【当耐特】
V
Visual Studio Blog
有赞技术团队
有赞技术团队
U
Unit 42
D
Docker
小众软件
小众软件
F
Full Disclosure
I
Intezer
Scott Helme
Scott Helme
P
Privacy International News Feed
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
Google DeepMind News
Google DeepMind News
B
Blog
Martin Fowler
Martin Fowler
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Vercel News
Vercel News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Spread Privacy
Spread Privacy
宝玉的分享
宝玉的分享
S
Security Affairs
www.infosecurity-magazine.com
www.infosecurity-magazine.com
月光博客
月光博客
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
Schneier on Security
Schneier on Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threat Research - Cisco Blogs
量子位
Hacker News - Newest:
Hacker News - Newest: "LLM"
H
Heimdal Security Blog
N
Netflix TechBlog - Medium
H
Hacker News: Front Page
P
Proofpoint News Feed
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
S
Schneier on Security

Sanonz

使用 Invox Setup 实现 Electron 安装包界面美化 用 VS Code 编辑器开发 STM32 嵌入式 使用 FontCreator 编辑字体库 | Sanonz 使用 React Context API 和 Hooks 实现全局状态管理和性能优化 使用 Phonegap + Cordova 搭建混合开发平台 利用 Github Actions 自动部署 Hexo 博客 在 WEB 端实现亮/暗主题跟随系统功能 | Sanonz Metalness vs Specular Workflows for PBR Shading 以太坊概念与使用 wep3/infura 实现以太币及合约币交易流程 | Sanonz 实现树莓派(Raspberry Pi)联网发送本机IP到绑定的微信 | Sanonz 使用 redux-store-provider 简化 react 开发流程 浏览器 HTTP 并发请求规则探讨 | Sanonz JavaScript Event Loop、Micro Task、Macro Task WebAssembly 从入门到吃鸡 | Sanonz Let's Encrypt 使用教程,免费的 SSL 证书,让你的网站拥抱 HTTPS Let's Encrypt 使用教程,免费的 SSL 证书,让你的网站拥抱 HTTPS Ubuntu 搭建 php、mysql、nginx 开发环境 | Sanonz WebGL 三维教程 - 绘制矩形和点(二) | Sanonz WebGL 三维教程 - WebGL 介绍(一) Hexo 搭建静态博客与 hexo-theme-concise 主题使用教程 | Sanonz
使用 Three.js 的 RenderTarget 实现离屏渲染
Sanonz · 2018-09-27 · via Sanonz

RenderTarget 介绍

在渲染的时候调用 renderer.render(scene, camera, renderTarget, forceClear) 方法,render 方法有四个参数,我们平时使用只传前两个参数,第一个参数是要绘制的场景,第二个参数是指定相机,相机照射的区域会转换成 2D 绘制到屏幕,而我们今天要讲的就是使用第三个参数,不让渲染的内容直接绘制到屏幕,而是存放到 renderTarget 里。RenderTarget 是一个缓冲区,用来记录渲染后的像素,而不是直接绘制到屏幕上,因此我们可以在绘制屏幕之前做一些处理,以满足特殊的需求。

把 RenderTarget 作为贴图使用

首先创建两个场景:一个 RTScene 场景,用来渲染 RenderTarget 贴图、另一个 Scene 场景,用来渲染最终显示到屏幕上的,并调用 RTScene 场景作为贴图。

创建一个尺寸为 300×300 大小的 RenderTarget 存放 RTScene 的渲染结果

1
2
const RT_SIZE = 300;
const RenderTarget = new THREE.WebGLRenderTarget( RT_SIZE, RT_SIZE);

创建相机

1
2
3
4
5
const Camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
Camera.position.x = 2;
Camera.position.y = 2;
Camera.position.z = 2;
Camera.lookAt(0, 0, 0);

创建 RTScene,并向 RTScene 场景添加灯光、环面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

const RTScene = new THREE.Scene();


const RTLight = new THREE.DirectionalLight( 0xffffff, 1.5 );
RTLight.position.set( 0, 1, 1 ).normalize();
RTScene.add(RTLight);


const geometryTorus = new THREE.TorusGeometry( 10, 3, 16, 100 );

const materialTorus = new THREE.LineBasicMaterial( {color: 0xff0000} );
const Torus = new THREE.Mesh( geometryTorus, materialTorus );
Torus.scale.set( 0.05, 0.05, 0.05 );
RTScene.add(Torus);

创建 Scene,并向 Scene 场景添加灯光、立方体,立方体使用 RTScene 作为贴图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Scene = new THREE.Scene();

const AmbientLight = new THREE.AmbientLight( 0xffffff, 1.5 );
Scene.add(AmbientLight);

const Light = new THREE.DirectionalLight( 0xffffff, 1.5 );
Light.position.set( 0, 1, 1 ).normalize();
Scene.add(Light);


const geometryBox = new THREE.BoxBufferGeometry( 1, 1, 1 );
const materialBox = new THREE.MeshStandardMaterial( {
color: 0xff9300,
map: RenderTarget.texture
} );
const Box = new THREE.Mesh( geometryBox, materialBox );
Scene.add(Box);

创建 Renderer

1
2
3
4
5
6
7
const Renderer = new THREE.WebGLRenderer( {
antialias: true
} );
Renderer.setClearColor(0xffffff, 1.0);
Renderer.setPixelRatio( window.devicePixelRatio );
Renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( Renderer.domElement );

场景设置完毕接下来我们就可以渲染场景了,注意这两个 render 的顺序,第一个的结果要给第二个使用,所以不能写反了

1
2
Renderer.render(RTScene, Camera, RenderTarget); 
Renderer.render(Scene, Camera);

如果场景有动画则需要使用 requestAnimationFrame 马不停蹄的进行渲染

1
2
3
4
5
6
7
8
9
10
11
12
animation();

function animation() {
Renderer.clear();

Box.rotation.y += 0.01;

Renderer.render(RTScene, Camera, RenderTarget);
Renderer.render(Scene, Camera);

requestAnimationFrame(animation);
}

实例 Demo

把 RenderTarget 画到 2D Canvas 上

我们可以使用 Renderer.readRenderTargetPixels(renderTarget, x, y, width, height, buffer) 方法读取离屏渲染的像素数据到一个 Uint8Array(length) 实例里,xy 可以限制读取的起点,widthheight 为读取的宽高。

新建一个画布,通过 canvas.getContext('2d') 方法获取 2D 的对象,此对象有个 ctx.putImageData(imagedata, dx, dy) 可以让画布绘制 ImageData(data, width, height) 实例类型的矩形像素,dataUint8ClampedArray(length) 实例,widthheight 为图片的宽和高(必须保证 Uint8ClampedArraylength = 4 * width * height 才不会报错)。

指定 canvas 元素,并获取 2D 对象

1
2
const preview = document.getElementById('preview');
const ctx = preview.getContext('2d');

创建 buffer 用来存放 RenderTarget 的像素数据,实例化 Uint8ClampedArray 以便创建 ImageData 时使用。在处理图片像素中与 Uint8Array 相比 Uint8ClampedArray 更安全,因为 Uint8ClampedArray 能保证写入的值在 0-255 之间,当写入的值小于 0 时会自动改为 0,当大于 255 时会自动改为 255。

1
2
const buffer = new Uint8Array(RT_SIZE * RT_SIZE * 4);
const clamped = new Uint8ClampedArray(buffer.buffer);
1
2
3
Renderer.readRenderTargetPixels(RenderTarget, 0, 0, RT_SIZE, RT_SIZE, buffer); 
const imageData = new ImageData(clamped, RT_SIZE, RT_SIZE);
ctx.putImageData(imageData, 0, 0);

实例 Demo

小结

至此结束,完整代码请查看例子,感谢阅读。