自学内容网 自学内容网

【Three.js基础学习】33.Halftone Shading shaders

前言

 半色调是一种用于印刷的技术,

    通过使用相同颜色但大小不同

    的点网格来创建看起来像渐变

    色的颜色变化。从远处看,各

    种半色调的组合会产生令人愉

    悦的色调。

    我们将重现这种效果,为我们的3D物体添加阴影和反射。

    基本着色器已经在:src/shaders/haftone/中

    在包括,文件夹中使用前几节课的轻型函数

    111-gu1 with elearColor and ucolor

    vite-plugin-glsl

    OrbitControls

    开始

    light 假如环境光 ,方向光

    实现网格

    gl_FragCoord是一个vec4,其中xy构成“屏幕”坐标,zw用于深度

    我们不在乎曼后两个

    注意当我观察颜色时,都是白色 ,要除以 分辨率(uResolution)

   

项目目录

实现思路示意图,跟之前学阴影 展示对应形状差不多,关键步骤代码有注释

一、代码

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Halftone Shading</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <canvas class="webgl"></canvas>
    <script type="module" src="./script.js"></script>
</body>
</html>

script.js

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import GUI from 'lil-gui'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import halftoneVertexShader from './shaders/halftone/vertex.glsl'
import halftoneFragmentShader from './shaders/halftone/fragment.glsl'

/* 
    半色调是一种用于印刷的技术,
    通过使用相同颜色但大小不同
    的点网格来创建看起来像渐变
    色的颜色变化。从远处看,各
    种半色调的组合会产生令人愉
    悦的色调。
    我们将重现这种效果,为我们的3D物体添加阴影和反射。
    基本着色器已经在:src/shaders/haftone/中
    在包括,文件夹中使用前几节课的轻型函数
    111-gu1 with elearColor and ucolor
    vite-plugin-glsl
    OrbitControls

    开始
    light 假如环境光 ,方向光

    实现网格
    gl_FragCoord是一个vec4,其中xy构成“屏幕”坐标,zw用于深度
    我们不在乎曼后两个
    注意当我观察颜色时,都是白色 ,要除以 分辨率(uResolution) 
    
*/


/**
 * Base
 */
// Debug
const gui = new GUI()

// Canvas
const canvas = document.querySelector('canvas.webgl')

// Scene
const scene = new THREE.Scene()

// Loaders
const gltfLoader = new GLTFLoader()

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight,
    pixelRatio: Math.min(window.devicePixelRatio, 2)
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight
    sizes.pixelRatio = Math.min(window.devicePixelRatio, 2)

    // update materials
    // 更新,当用户调整窗口大小时候,将分辨率发送到着色器,更新
    material.uniforms.uResolution.value.set(sizes.width * sizes.pixelRatio,sizes.height * sizes.pixelRatio)

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(sizes.pixelRatio)
})

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(25, sizes.width / sizes.height, 0.1, 100)
camera.position.x = 7
camera.position.y = 7
camera.position.z = 7
scene.add(camera)

// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true

/**
 * Renderer
 */
const rendererParameters = {}
rendererParameters.clearColor = '#26132f'

const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true
})
renderer.setClearColor(rendererParameters.clearColor)
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(sizes.pixelRatio)

gui
    .addColor(rendererParameters, 'clearColor')
    .onChange(() =>
    {
        renderer.setClearColor(rendererParameters.clearColor)
    })

/**
 * Material
 */
const materialParameters = {}
materialParameters.color = '#ff794d'
materialParameters.shadowColor = '#8e19b8'
materialParameters.lightColor = '#e5ffe0'

const material = new THREE.ShaderMaterial({
    vertexShader: halftoneVertexShader,
    fragmentShader: halftoneFragmentShader,
    uniforms:
    {
        uColor: new THREE.Uniform(new THREE.Color(materialParameters.color)),
        uShadeColor: new THREE.Uniform(new THREE.Color(materialParameters.shadowColor)),
        uResolution: new THREE.Uniform(new THREE.Vector2(sizes.width * sizes.pixelRatio,sizes.height * sizes.pixelRatio)), // 拥有尺寸,同时乘以像素比因为 要拥有相同比例
        uShadowRepetitions:new THREE.Uniform(100),
        uShadowColor:new THREE.Uniform(new THREE.Color(materialParameters.shadowColor)),
        uLightRepetitions:new THREE.Uniform(130),
        uLightColor:new THREE.Uniform(new THREE.Color(materialParameters.lightColor))
    }
})

gui
    .addColor(materialParameters, 'color')
    .onChange(() =>
    {
        material.uniforms.uColor.value.set(materialParameters.color)
    })

gui
    .add(material.uniforms.uShadowRepetitions, 'value')
    .min(1)
    .max(300)
    .step(1)

gui
    .addColor(materialParameters, 'shadowColor')
    .onChange(() =>
    {
        material.uniforms.uShadowColor.value.set(materialParameters.shadowColor)
    })

gui
    .add(material.uniforms.uLightRepetitions, 'value')
    .min(1)
    .max(300)
    .step(1)

gui
    .addColor(materialParameters, 'lightColor')
    .onChange(() =>
    {
        material.uniforms.uLightColor.value.set(materialParameters.lightColor)
    })

/**
 * Objects
 */
// Torus knot
const torusKnot = new THREE.Mesh(
    new THREE.TorusKnotGeometry(0.6, 0.25, 128, 32),
    material
)
torusKnot.position.x = 3
scene.add(torusKnot)

// Sphere
const sphere = new THREE.Mesh(
    new THREE.SphereGeometry(),
    material
)
sphere.position.x = - 3
scene.add(sphere)

// Suzanne
let suzanne = null
gltfLoader.load(
    './suzanne.glb',
    (gltf) =>
    {
        suzanne = gltf.scene
        suzanne.traverse((child) =>
        {
            if(child.isMesh)
                child.material = material
        })
        scene.add(suzanne)
    }
)

/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // Rotate objects
    if(suzanne)
    {
        suzanne.rotation.x = - elapsedTime * 0.1
        suzanne.rotation.y = elapsedTime * 0.2
    }

    sphere.rotation.x = - elapsedTime * 0.1
    sphere.rotation.y = elapsedTime * 0.2

    torusKnot.rotation.x = - elapsedTime * 0.1
    torusKnot.rotation.y = elapsedTime * 0.2

    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

style.css

*
{
    margin: 0;
    padding: 0;
}

html,
body
{
    overflow: hidden;
}

.webgl
{
    position: fixed;
    top: 0;
    left: 0;
    outline: none;
}

fragment.glsl

uniform vec3 uColor;
uniform vec2 uResolution;
uniform float uShadowRepetitions;
uniform vec3 uShadowColor;
uniform float uLightRepetitions;
uniform vec3 uLightColor;

varying vec3 vNormal;
varying vec3 vPosition;

#include ../includes/directionalLight.glsl
#include ../includes/ambientLight.glsl

vec3 halftone( // 创建半色调颜色函数
    vec3 color, // 颜色
    float repetitions, // 大小
    vec3 direction, // 方向
    float low, // 界限值
    float high, // 界限值
    vec3 pointColor, // 半色调颜色
    vec3 normal // 法线
){
    float insensity = dot(normal,direction); // 强度  这部分之前的代码做过,法线和光()的方向
    insensity = smoothstep(low,high,insensity); // 平滑过渡

    vec2 uv = gl_FragCoord.xy / uResolution.y ; // 完美的像素比(uResolution)
    uv *= repetitions;
    uv = mod(uv,1.0);

    // 我们需要距离紫外线电池中心的距离。我们可以使用距离函数(distance)与vec2(0.5)相比
    float point = distance(uv,vec2(0.5));
    point = 1.0 - step(0.5 * insensity,point);

    
    return  mix(color,pointColor,point * 0.5); // 将颜色混合在一起,

}

/* 
    mod(x, y)返回x和y的模
    利用取模函数mod()达到反复渐变效果

    distance(p0,p1)计算向量p0,p1之间的距离

    step(edge, x)如果x < edge,返回0.0,否则返回1.0

    dot向量x,y之间的点积

    平稳过渡
    smoothstep(edge0, edge1, x)
    如果x <= edge0,返回0.0 ;
    如果x >= edge1 返回1.0;
    如果edge0 < x < edge1,则执行0~1之间的平滑埃尔米特差值。
    如果edge0 >= edge1,结果是未定义的

    mix(x, y, a)返回线性混合的x和y,如:x*(1−a)+y*a

 */

void main()
{
    vec3 viewDirection = normalize(vPosition - cameraPosition); // 光的反射 视野方向
    vec3 normal = normalize(vNormal);
    vec3 color = uColor;

    // light
    vec3 light = vec3(0.0);
    light += ambientLight(
        vec3(1.0),
        1.0
    );
    light += directionalLight(
        vec3(1.0,1.0,1.0),
        1.0,
        normal,
        vec3(1.0,1.0,0.0),
        viewDirection,
        1.0
    );

    color *= light;

    // Halftone 除以像素比 获得最终颜色
    // float repetitions = 50.0;
    // vec3 direction = vec3(0.0, -1.0 , 0.0); // 方向
    // float low = -0.8; // 界限值 展示半色调
    // float high = 1.5; // 界限值
    // vec3 pointColor = vec3(1.0,0.0,0.0);

    color = halftone(
        color,
        uShadowRepetitions,
        vec3(0.0, -1.0 , 0.0),
        -0.8,
        1.5,
        uShadowColor,
        normal
    );
    color = halftone(
        color,
        uLightRepetitions,
        vec3(1.0, 1.0 , 0.0),
        0.5,
        1.5,
        uLightColor,
        normal
    );

    // Final color
    gl_FragColor = vec4(color, 1.0);
    #include <tonemapping_fragment>
    #include <colorspace_fragment>
}

vertex.glsl

varying vec3 vNormal;
varying vec3 vPosition;

void main()
{
    // Position
    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
    gl_Position = projectionMatrix * viewMatrix * modelPosition;

    // Model normal
    vec3 modelNormal = (modelMatrix * vec4(normal, 0.0)).xyz;

    // Varyings
    vNormal = modelNormal;
    vPosition = modelPosition.xyz;
}

ambientLight.glsl

vec3 ambientLight(vec3 lightColor, float lightIntensity)
{
    return lightColor * lightIntensity;
}

directionalLight.glsl

vec3 directionalLight(vec3 lightColor, float lightIntensity, vec3 normal, vec3 lightPosition, vec3 viewDirection, float specularPower)
{
    vec3 lightDirection = normalize(lightPosition);
    vec3 lightReflection = reflect(- lightDirection, normal);

    // Shading
    float shading = dot(normal, lightDirection);
    shading = max(0.0, shading);

    // Specular
    float specular = - dot(lightReflection, viewDirection);
    specular = max(0.0, specular);
    specular = pow(specular, specularPower);

    return lightColor * lightIntensity * (shading + specular);
}

pointLight.glsl

vec3 pointLight(vec3 lightColor, float lightIntensity, vec3 normal, vec3 lightPosition, vec3 viewDirection, float specularPower, vec3 position, float lightDecay)
{
    vec3 lightDelta = lightPosition - position;
    float lightDistance = length(lightDelta);
    vec3 lightDirection = normalize(lightDelta);
    vec3 lightReflection = reflect(- lightDirection, normal);

    // Shading
    float shading = dot(normal, lightDirection);
    shading = max(0.0, shading);

    // Specular
    float specular = - dot(lightReflection, viewDirection);
    specular = max(0.0, specular);
    specular = pow(specular, specularPower);

    // Decay
    float decay = 1.0 - lightDistance * lightDecay;
    decay = max(0.0, decay);

    return lightColor * lightIntensity * decay * (shading + specular);
}

二、效果

半色调颜色-阴影 着色器


总结

半色调颜色 着色器的应用!


原文地址:https://blog.csdn.net/weixin_41722662/article/details/144749817

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!