当前位置: 首页 > news >正文

合肥网站建设yjhlw福州做网站哪家好

合肥网站建设yjhlw,福州做网站哪家好,怎样做返利网站,网站建设分哪些类别本文使用Three.js的后处理创建粗略的铅笔画效果。我们将完成创建自定义后处理渲染通道、在 WebGL中实现边缘检测、将法线缓冲区重新渲染到渲染目标以及使用生成和导入的纹理调整最终结果的步骤。翻译自Codrops#xff0c;有改动。 Three.js 中的后处理 Three.js中的后处理是一… 本文使用Three.js的后处理创建粗略的铅笔画效果。我们将完成创建自定义后处理渲染通道、在 WebGL中实现边缘检测、将法线缓冲区重新渲染到渲染目标以及使用生成和导入的纹理调整最终结果的步骤。翻译自Codrops有改动。 Three.js 中的后处理 Three.js中的后处理是一种在绘制场景后将效果应用于渲染场景的方法。除了Three.js提供的所有开箱即用的后处理效果外还可以通过创建自定义渲染通道来添加我们自己的滤镜。 自定义渲染过程本质上是一个函数它接收场景图像并返回一个新图像并应用所需的效果。我们可以将这些渲染通道想象成Photoshop中的图层效果————每个渲染通道都基于之前的效果输出应用新的滤镜。生成的图像是所有不同效果滤镜的组合。 在 Three.js 中启用后处理 要向我们的场景添加后处理效果我们需要设置EffectComposer来进行场景渲染。这个EffectComposer将后处理效果按传递顺序叠加在一起。如果我们想让我们渲染的场景传递给下一个效果我们需要先利用RenderPass创建一个后处理通道。 然后在启动渲染循环的tick函数中我们调用composer.render()来代替renderer.render(scene, camera)。 const renderer new THREE.WebGLRenderer()const composer new EffectComposer(renderer) const renderPass new RenderPass(scene, camera)composer.addPass(renderPass)function tick() {requestAnimationFrame(tick)composer.render() }tick()有两种创建自定义后处理效果的方法 1.创建自定义着色器并将其传递给ShaderPass实例或者 2.通过扩展Pass类来创建自定义渲染通道。 因为我们希望我们的后处理效果获得比uniform和attribute更多的信息所以我们将创建一个自定义渲染通道。 创建自定义渲染通道 一个自定义通道继承自Pass类并具有三个方法setSize、render和dispose我们将主要关注render方法。 首先我们扩展Pass类来创建自己的PencilLinesPass类然后再实现我们自己的渲染逻辑。 import { Pass, FullScreenQuad } from three/examples/jsm/postprocessing/Pass import * as THREE from threeexport class PencilLinesPass extends Pass {constructor() {super()}render(renderer: THREE.WebGLRenderer,: THREE.WebGLRenderTarget,readBuffer: THREE.WebGLRenderTarget) {if (this.renderToScreen) {renderer.setRenderTarget(null)} else {renderer.setRenderTarget(writeBuffer)if (this.clear) renderer.clear()}} }从上面代码中可以看出该render方法接受一个WebGLRenderer对象和两个WebGLRenderTarget对象一个用于写入缓冲区另一个用于读取缓冲区。在Three.js中渲染目标一般是我们可以渲染到场景的纹理它们用于在通道之间发送数据。readBuffer从先前的渲染通道接收数据在我们的例子中是默认的RenderPass;writeBuffer则是将数据发送到下一个渲染通道。 当renderToScreen为true的时候则意味着我们要将缓冲区发送到屏幕而不是渲染目标。渲染器的渲染目标设置为null的时候默认就是为屏幕画布。 在这一点上我们实际上并没有渲染任何东西甚至没有通过readBuffer传入数据。为了渲染场景事物我们需要创建一个FullscreenQuad和一个负责渲染的着色器材质然后将着色器材质渲染到FullscreenQuad。 为了测试一切设置是否正确我们可以使用threejs内置的CopyShader来显示我们放入其中的任何图像。 import { Pass, FullScreenQuad } from three/examples/jsm/postprocessing/Pass import { CopyShader } from three/examples/jsm/shaders/CopyShader import * as THREE from threeexport class PencilLinesPass extends Pass {fsQuad: FullScreenQuadmaterial: THREE.ShaderMaterialconstructor() {super()this.material new THREE.ShaderMaterial(CopyShader)this.fsQuad new FullScreenQuad(this.material)}dispose() {this.material.dispose()this.fsQuad.dispose()}render(renderer: THREE.WebGLRenderer,writeBuffer: THREE.WebGLRenderTarget,readBuffer: THREE.WebGLRenderTarget) {this.material.uniforms[tDiffuse].value readBuffer.textureif (this.renderToScreen) {renderer.setRenderTarget(null)this.fsQuad.render(renderer)} else {renderer.setRenderTarget(writeBuffer)if (this.clear) renderer.clear()this.fsQuad.render(renderer)}} }注意我们将uniform变量tDiffuse传递给着色器材质。CopyShader已经内置了这个uniform它代表要在屏幕上渲染显示的图像。如果你正在编写自己的ShaderPass这个uniform将自动传递到你的着色器中。 剩下的就是通过将自定义渲染通道添加到EffectComposer来将自定义渲染通道连接到场景中而且注意要在添加完RenderPass之后。 const renderPass new RenderPass(scene, camera) const pencilLinesPass new PencilLinesPass()composer.addPass(renderPass) composer.addPass(pencilLinesPass)查看 Codesandbox 示例 具有自定义渲染通道和 CopyShader 的场景 用于创建轮廓的 Sobel 算子 我们需要能够告诉计算机根据我们的输入图像即场景图像检测边缘线条我们将使用的这种边缘检测称为 Sobel 算子。 Sobel 算子通过查看图像一小部分的梯度来进行边缘检测————本质上是检查从一个值到另一个值的过渡有多尖锐。图像被分解成更小的“内核”比如说是 3px x 3px 的正方形其中中心像素是当前正在处理的像素。下图显示了它的样子中心的红色方块代表当前正在评估的像素其余方块是它的邻近像素。 3px x 3px 内核 然后通过获取像素值亮度并将其乘以基于其相对于被评估像素的位置的权重来计算每个邻近像素的加权值。这是通过权重在水平和垂直方向上偏置梯度来完成的。取两个值的平均值如果它超过某个阈值我们认为该像素表示边缘。 Sobel 算子的水平和垂直梯度 Three.js 已经为我们提供了SobelOperatorShader中的代码我们可以将这段代码复制到我们的着色器材质中。 实现 Sobel 算子 我们现在需要添加我们自己的ShaderMaterial来代替CopyShader以便我们可以控制顶点和片段着色器以及发送给那些着色器的uniform。 // PencilLinesMaterial.ts export class PencilLinesMaterial extends THREE.ShaderMaterial {constructor() {super({uniforms: {tDiffuse: { value: null },// 我们稍后会在这里传递画布大小uResolution: {value: new THREE.Vector2(1, 1)}},fragmentShader, vertexShader})} }然后我们需要在场景中使用我们的新着色器材质。 // PencilLinesPass.ts export class PencilLinesPass extends Pass {fsQuad: FullScreenQuadmaterial: PencilLinesMaterialconstructor({ width, height }: { width: number; height: number }) {super()// 将材质更改为我们新的PencilLinesMaterialthis.material new PencilLinesMaterial() this.fsQuad new FullScreenQuad(this.material)// 将 uResolution 设置为当前画布的宽度和高度this.material.uniforms.uResolution.value new THREE.Vector2(width, height)} }接下来我们可以编写顶点和片段着色器。 除了设置gl_Position并将uv属性传递给片段着色器之外顶点着色器并没有做其他事情。因为我们将图像渲染到FullscreenQuad所以uv信息对应于任何给定片段在屏幕上的位置。 // vertex shader varying vec2 vUv;void main() {vUv uv;gl_Position projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }片元着色器要复杂一些所以我们逐行进行分解。首先我们要使用Three.js已经提供的实现算法来实现Sobel算子。唯一的区别是我们想要控制我们如何计算每个像素的值因为我们也将引入法线缓冲区的线检测。 float combinedSobelValue() {// 内核定义在 glsl 中矩阵按列优先顺序填充const mat3 Gx mat3(-1, -2, -1, 0, 0, 0, 1, 2, 1);// x方向内核const mat3 Gy mat3(-1, 0, 1, -2, 0, 2, -1, 0, 1);// y方向内核// 获取片段的 3x3 邻域// 第一列float tx0y0 getValue(-1, -1);float tx0y1 getValue(-1, 0);float tx0y2 getValue(-1, 1);// 第二列float tx1y0 getValue(0, -1);float tx1y1 getValue(0, 0);float tx1y2 getValue(0, 1);// 第三列float tx2y0 getValue(1, -1);float tx2y1 getValue(1, 0);float tx2y2 getValue(1, 1);// x方向的梯度值float valueGx Gx[0][0] * tx0y0 Gx[1][0] * tx1y0 Gx[2][0] * tx2y0 Gx[0][1] * tx0y1 Gx[1][1] * tx1y1 Gx[2][1] * tx2y1 Gx[0][2] * tx0y2 Gx[1][2] * tx1y2 Gx[2][2] * tx2y2;// y方向的梯度值float valueGy Gy[0][0] * tx0y0 Gy[1][0] * tx1y0 Gy[2][0] * tx2y0 Gy[0][1] * tx0y1 Gy[1][1] * tx1y1 Gy[2][1] * tx2y1 Gy[0][2] * tx0y2 Gy[1][2] * tx1y2 Gy[2][2] * tx2y2;// 总梯度的大小float G (valueGx * valueGx) (valueGy * valueGy);return clamp(G, 0.0, 1.0); }我们将当前像素的偏移量传递给getValue函数在获取邻域像素的值。目前我们仅需要评估漫反射缓冲区的值我们将在下一步中添加法线缓冲区。 float valueAtPoint(sampler2D image, vec2 coord, vec2 texel, vec2 point) {vec3 luma vec3(0.299, 0.587, 0.114);return dot(texture2D(image, coord texel * point).xyz, luma); }float diffuseValue(int x, int y) {return valueAtPoint(tDiffuse, vUv, vec2(1.0 / uResolution.x, 1.0 / uResolution.y), vec2(x, y)) * 0.6; }float getValue(int x, int y) {return diffuseValue(x, y); }该valueAtPoint函数可以输入任何纹理漫反射或法线并返回指定点的灰度值。luma向量用于计算颜色的亮度从而将rgb颜色转换为灰度值。这个实现来自glsl-luma。 因为getValue函数只考虑漫反射缓冲区这意味着场景中的任何边缘都将被检测到包括由投射的阴影创建的边缘。这也意味着例如物体的轮廓如果它们与周围环境投射的阴影融合得太好可能会被忽略。为了捕获那些缺失的边缘我们接下来将从法线缓冲区添加边缘检测。 最后我们在主函数中调用 Sobel 算子如下所示 void main() {float sobelValue combinedSobelValue();sobelValue smoothstep(0.01, 0.03, sobelValue);vec4 lineColor vec4(0.32, 0.12, 0.2, 1.0);if (sobelValue 0.1) {gl_FragColor lineColor;} else {gl_FragColor vec4(1.0);} }查看 Codesandbox 示例 创建一个法线缓冲区渲染 为了获得合适的轮廓Sobel算子通常应用于场景的法线和深度缓冲区因此会捕获对象的轮廓但不会捕获对象内的线条。Omar Shehata 在他的How to render outlines in WebGL教程中描述了这种方法。出于只是实现粗略铅笔效果的目的我们不需要完整的边缘检测但我们确实希望使用法线来获得更完整的边缘。 由于法线是表示对象表面每个点方向的向量因此通常用颜色表示以获取包含场景中所有法线数据的图像。这张图被称为“法线缓冲区”。 为了创建一个法线缓冲区首先我们需要在PencilLinesPass构造函数中创建一个新的渲染目标。我们还需要在类上创建一个MeshNormalMaterial因为我们将在渲染法线缓冲区时使用它来覆盖场景的默认材质。 const normalBuffer new THREE.WebGLRenderTarget(width, height)normalBuffer.texture.format THREE.RGBAFormat normalBuffer.texture.type THREE.HalfFloatType normalBuffer.texture.minFilter THREE.NearestFilter normalBuffer.texture.magFilter THREE.NearestFilter normalBuffer.texture.generateMipmaps false normalBuffer.stencilBuffer false this.normalBuffer normalBufferthis.normalMaterial new THREE.MeshNormalMaterial()为了渲染通道内的场景我们还需要通过渲染通道的构造函数来传入scene和camera。 // PencilLinesPass.ts 构造函数 constructor({ ..., scene, camera}: { ...; scene: THREE.Scene; camera: THREE.Camera }) {super()this.scene scenethis.camera camera... }在渲染通道的render方法中我们想要使用覆盖默认材质的法线材质重新渲染场景。我们将renderTarget设置为normalBuffer并像往常一样使用WebGLRenderer渲染场景。唯一的区别是渲染器不是使用场景的默认材质渲染到屏幕而是使用法线材质渲染到我们的渲染目标此处即为我们的normalBuffer。然后我们将normalBuffer.texture传递给着色器材质。overrideMaterial参数表示强制使用定义的材质渲染场景中的所有内容。 renderer.setRenderTarget(this.normalBuffer) const overrideMaterialValue this.scene.overrideMaterialthis.scene.overrideMaterial this.normalMaterial renderer.render(this.scene, this.camera) this.scene.overrideMaterial overrideMaterialValuethis.material.uniforms.uNormals.value this.normalBuffer.texture this.material.uniforms.tDiffuse.value readBuffer.texture如果此时我们利用texture2D(uNormals,vUv);将法线缓冲区的值赋给gl_FragColor渲染结果将是下图所示 当前场景的法线缓冲区 在自定义材质的片段着色器中我们修改getValue函数让它包含漫反射缓冲区和法线缓冲区的 Sobel 算子。如果我们在这里只计算法线缓冲区会发现平面阴影的边缘就没有了因为平面法线是没有过渡的。 float normalValue(int x, int y) {return valueAtPoint(uNormals, vUv, vec2(1.0 / uResolution.x, 1.0 / uResolution.y), vec2(x, y)) * 0.3; }float getValue(int x, int y) {return diffuseValue(x, y) normalValue(x, y); }查看 Codesandbox 示例 为着色和波浪线添加生成的纹理噪声 有两种方法可以将噪声带入后处理效果 通过在着色器中由程序生成噪声或者通过使用带有噪声的图像并将其应用为纹理。 两者都提供了不同级别的灵活性和控制。对于噪声函数我们使用Inigo Quilez的梯度噪声实现算法因为它在应用于“着色”效果时提供了很好的噪声均匀性。 这个噪声函数是在获取Sobel算子的值时调用的并专门作用于法线值所以片段着色器中getValue的函数变化如下 float getValue(int x, int y) {float noiseValue noise(gl_FragCoord.xy);noiseValue noiseValue * 2.0 - 1.0;noiseValue * 10.0;return diffuseValue(x, y) normalValue(x, y) * noiseValue; }这样得出来的结果是在法向量值发生变化时对象曲线上形成带纹理的铅笔线和点画效果。请注意平面对象如Plane不会产生这些效果因为它们的法线值没有任何变化。 此效果的下一步也是最后一步是为线条添加扭曲。为此我们使用了在Photoshop中使用渲染云效果创建的纹理文件。 在 Photoshop 中创建的生成的云纹理 云纹理通过一个uniform变量传递给着色器与漫反射和法线缓冲区的方式相同。一旦着色器可以访问纹理我们就可以对每个片段的纹理进行采样并使用它来偏移我们在缓冲区中读取的位置。本质上我们通过扭曲我们正在读取的图像来获得波浪线效果。因为纹理的噪点是平滑的线条不会出现锯齿状和不规则的情况。 float normalValue(int x, int y) {float cutoff 50.0;float offset 0.5 / cutoff;float noiseValue clamp(texture(uTexture, vUv).r, 0.0, cutoff) / cutoff - offset;return valueAtPoint(uNormals, vUv noiseValue, vec2(1.0 / uResolution.x, 1.0 / uResolution.y), vec2(x, y)) * 0.3; }查看 Codesandbox 示例 结论 有许多技术可以在3D中创建手绘或素描效果。我们可以通过基于噪声纹理调制被认为是边缘的阈值来调整线条粗细。我们还可以将Sobel算子应用于深度缓冲区完全忽略漫反射缓冲区以获得没有轮廓阴影的轮廓对象。我们可以根据场景中的照明信息而不是基于对象的法线来添加生成的噪声。接下来我会将这种效果应用到cesium和mapbox上。
http://www.hkea.cn/news/14483456/

相关文章:

  • 昆山网站公司哪家好门户网站那个程序比较
  • 广东省建设厅投诉网站首页网站建设流程有哪七步
  • 网站开发和小程序开发区别二手商标网
  • 稳健 安全的网站设计制作手机网站制作器
  • 做网站改变图片位置免费的舆情网站入口有哪些
  • 哪里有制作网站服务竞价外包托管费用
  • 自建站系统凡科登录入口下载
  • 珠海专业机械网站建设wordpress更新很慢
  • 墙蛙网站谁家做的seoul怎么读
  • 广州免费建站哪里有最近最新mv在线观看免费高清
  • 创新的宁波网站建设电子商务职业生涯规划书
  • 响应式装饰设计公司网站源码广州网站推广建设
  • 在百度做网站推广怎么做做非法网站会怎样
  • 济南建站详情wordpress 主题慢
  • 网站的表格参数怎么做用c 做一个小网站怎么做
  • 建设网站用什么网络好wordpress comments_popup_link
  • 在哪个网站做推广比较好网上团建智慧团建登录入口
  • 电子商务网站功能一般网站建设公司好
  • 网站loading动画电子商务网站建设一般流程
  • 河南住房和城乡建设厅一体化平台网站个人网页设计说明500字
  • php图片展示网站佛山网站建设佛山
  • 网站打不开了怎么办建e网官方网站
  • 自己电脑上做网站怎么使用源码个人微信号做网站行吗
  • 桂林网站制作公司华彩网站定制化价格
  • 国内最大的c2c网站是网站后台发邮件
  • 湖南响应式网站建设公司简介宣传
  • 关于春节的网站设计html重庆市建设银行网站首页
  • 自助建站系统个人网站网站简介 title
  • 建一个商城网站多少钱宁波模板建站定制网站
  • iis7.5 查看网站流量响应式网站介绍