使用three.js 实现着色器草地的效果

news2024/9/27 9:25:09

使用three.js 实现着色器草地的效果
在这里插入图片描述

在线预览https://z2586300277.github.io/three-cesium-examples/#/codeMirror?navigation=ThreeJS&classify=shader&id=grassShader

在 https://threehub.cn 中还有很多案例

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

const box = document.getElementById('box')
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, box.clientWidth / box.clientHeight, 0.1, 1000)
camera.position.set(0, 10, 10)
const renderer = new THREE.WebGLRenderer({ antialias: true , alpha: true, logarithmicDepthBuffer: true})
renderer.setSize(box.clientWidth, box.clientHeight)
box.appendChild(renderer.domElement)
new OrbitControls(camera, renderer.domElement)
window.onresize = () => {
    renderer.setSize(box.clientWidth, box.clientHeight)
    camera.aspect = box.clientWidth / box.clientHeight
    camera.updateProjectionMatrix()
}
scene.background = new THREE.CubeTextureLoader().load([0, 1, 2, 3, 4, 5].map(k => (`https://z2586300277.github.io/3d-file-server/` + 'files/sky/skyBox0/' + (k + 1) + '.png')));

let grass = null
animate()
function animate(time) {
   if(grass) grass.update(time);
    requestAnimationFrame(animate)
    renderer.render(scene, camera)
}

const BLADE_WIDTH = 0.1
const BLADE_HEIGHT = 0.8
const BLADE_HEIGHT_VARIATION = 0.6
const BLADE_VERTEX_COUNT = 5
const BLADE_TIP_OFFSET = 0.1

function interpolate(val, oldMin, oldMax, newMin, newMax) {
  return ((val - oldMin) * (newMax - newMin)) / (oldMax - oldMin) + newMin
}

class GrassGeometry extends THREE.BufferGeometry {
  constructor(size, count) {
    super()

    const positions = []
    const uvs = []
    const indices = []

    for (let i = 0; i < count; i++) {
      const surfaceMin = (size / 2) * -1
      const surfaceMax = size / 2
      const radius = (size / 2) * Math.random()
      const theta = Math.random() * 2 * Math.PI

      const x = radius * Math.cos(theta)
      const y = radius * Math.sin(theta)

      uvs.push(
        ...Array.from({ length: BLADE_VERTEX_COUNT }).flatMap(() => [
          interpolate(x, surfaceMin, surfaceMax, 0, 1),
          interpolate(y, surfaceMin, surfaceMax, 0, 1)
        ])
      )

      const blade = this.computeBlade([x, 0, y], i)
      positions.push(...blade.positions)
      indices.push(...blade.indices)
    }

    this.setAttribute(
      'position',
      new THREE.BufferAttribute(new Float32Array(positions), 3)
    )
    this.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2))
    this.setIndex(indices)
    this.computeVertexNormals()
  }

  // Grass blade generation, covered in https://smythdesign.com/blog/stylized-grass-webgl
  // TODO: reduce vertex count, optimize & possibly move to GPU
  computeBlade(center, index = 0) {
    const height = BLADE_HEIGHT + Math.random() * BLADE_HEIGHT_VARIATION
    const vIndex = index * BLADE_VERTEX_COUNT

    // Randomize blade orientation and tip angle
    const yaw = Math.random() * Math.PI * 2
    const yawVec = [Math.sin(yaw), 0, -Math.cos(yaw)]
    const bend = Math.random() * Math.PI * 2
    const bendVec = [Math.sin(bend), 0, -Math.cos(bend)]

    // Calc bottom, middle, and tip vertices
    const bl = yawVec.map((n, i) => n * (BLADE_WIDTH / 2) * 1 + center[i])
    const br = yawVec.map((n, i) => n * (BLADE_WIDTH / 2) * -1 + center[i])
    const tl = yawVec.map((n, i) => n * (BLADE_WIDTH / 4) * 1 + center[i])
    const tr = yawVec.map((n, i) => n * (BLADE_WIDTH / 4) * -1 + center[i])
    const tc = bendVec.map((n, i) => n * BLADE_TIP_OFFSET + center[i])

    // Attenuate height
    tl[1] += height / 2
    tr[1] += height / 2
    tc[1] += height

    return {
      positions: [...bl, ...br, ...tr, ...tl, ...tc],
      indices: [
        vIndex,
        vIndex + 1,
        vIndex + 2,
        vIndex + 2,
        vIndex + 4,
        vIndex + 3,
        vIndex + 3,
        vIndex,
        vIndex + 2
      ]
    }
  }
}

const cloudTexture = new THREE.TextureLoader().load(`https://z2586300277.github.io/3d-file-server/` + 'threeExamples/shader/cloud.jpg')
cloudTexture.wrapS = cloudTexture.wrapT = THREE.RepeatWrapping

class Grass extends THREE.Mesh {
  constructor(size, count) {
    const geometry = new GrassGeometry(size, count)
    const material = new THREE.ShaderMaterial({
      uniforms: {
        uCloud: { value: cloudTexture },
        offsetX: { value: 0.5 },
        offsetY: { value: 0.3 },
        uTime: { value: 0 },
      },
      side: THREE.DoubleSide,
      vertexShader:`  uniform float uTime;
      uniform float offsetX;
      uniform float offsetY;
    
      varying vec3 vPosition;
      varying vec2 vUv;
      varying vec3 vNormal;
    
      float wave(float waveSize, float tipDistance, float centerDistance) {
        // Tip is the fifth vertex drawn per blade
        bool isTip = (gl_VertexID + 1) % 5 == 0;
    
        float waveDistance = isTip ? tipDistance : centerDistance;
        return sin((uTime / 500.0) + waveSize) * waveDistance;
      }
    
      void main() {
        vPosition = position;
        vUv = uv;
        
        // Cloud shadow move
        vUv.x += uTime * 0.0001 * offsetX;
        vUv.y += uTime * 0.0001 * offsetY;
    
        vNormal = normalize(normalMatrix * normal);
        if (vPosition.y < 0.0) {
          vPosition.y = 0.0;
        } else {
          vPosition.x += wave(uv.x * 10.0, 0.3, 0.1);      
        }
        gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0);
      }`,
      fragmentShader:`  uniform sampler2D uCloud;
      uniform float uTime;
      varying vec3 vPosition;
      varying vec2 vUv;
      varying vec3 vNormal;
    
      vec3 green = vec3(0.2, 0.6, 0.3);
    
      void main() {
        vec3 color = mix(green * 0.7, green, vPosition.y);
        color = mix(color, texture2D(uCloud, vUv).rgb, 0.4);
        float lighting = normalize(dot(vNormal, vec3(10)));
        gl_FragColor = vec4(color + lighting * 0.03, 1.0);
      }`,
    })
    super(geometry, material)
    const floor = new THREE.Mesh(
      new THREE.CircleGeometry(size / 2, 8).rotateX(Math.PI / 2),
      material
    )
    floor.position.y = -Number.EPSILON
    this.add(floor)

  }
  update(time) {
    this.material.uniforms.uTime.value = time
  }
}

grass = new Grass(50, 100000);
scene.add(grass);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2165791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java基础(Arrays工具类)(asList()方法)(详细)

目录 一、Arrays工具类 &#xff08;1&#xff09;引言 &#xff08;2&#xff09;基本介绍 &#xff08;3&#xff09;主要功能&#xff08;提供的方法&#xff09; &#xff08;I&#xff09;排序&#xff08;Arrays.sort()&#xff09; &#xff08;II&#xff09;搜索(查找…

IDEA自动清理类中未使用的import包

目录 1.建议清理包的理由 2.清理未使用包的方式 2.1 手动快捷键清理 2.2 设置自动清理 1.建议清理包的理由 有时候项目类文件中会有很多包被引入了&#xff0c;但是并没有被使用&#xff0c;这会增加项目的编译时间并且代码可读性也会变差。在开发过程中&#xff0c;建议设…

ISP去噪(3)_图像的高频分量和低频分量

#图像分析# 总是不好确认头发和毛绒玩具到底是低频还是高频分量。现在得出结论&#xff0c;头发和毛绒玩具都是高频信息&#xff0c;因为细节很多。 目录 图像的频率 (1) 什么是低频? (2) 什么是高频? &#xff08;3&#xff09;低频和高频对比 &#xff08;4&#xf…

谷歌今天发布了两款升级版Gemini模型:Gemini-1.5-Pro-002和Gemini-1.5-Flash-002

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

免杀笔记 ---> 无痕Hook?硬件断点 Syscall!

说到Hook&#xff0c;我们有很多Hook&#xff0c;像Inline-Hook&#xff0c;我们也是用的比较多&#xff0c;但是正如我上一篇Blog说的&#xff0c;他会对内存进行修改&#xff0c;如果EDR或者AV增加一个校验机制&#xff0c;不断检验某一块内存&#xff0c;那么就算你用syscal…

Flink 结合kafka 实现端到端的一致性原理

Kafka 事务实现原理 Flink checkpoint 结合kafka 实现端到端的一致性 为啥taskState 的时候要开启一个新事务&#xff0c;因为本来做state 就相当于把barrier之前的状态做一个快照&#xff0c;相当于是一个新的开始&#xff0c;所以开启一个新的事务。那为啥checkpoint 第一步要…

如何在数据分析中处理异常?

在数据分析中&#xff0c;处理异常值是确保数据质量的关键步骤。以下是一些常见的方法&#xff1a; 1. 检测异常值 可视化方法 箱线图&#xff1a;通过matplotlib或seaborn绘制箱线图&#xff0c;识别数据中的异常值。 import seaborn as sns import matplotlib.pyplot as …

新版双向链表,添加了at, front, back, insert, emplace等为了兼容std.

例子&#xff1a; #define _list _DListint main() {list<int> c1 { 1,2,3,4,5,6,7,8,9,10};if (!c1.empty()) {c1.front() 42; //将42赋予c中的一个元素auto& v c1.back(); //获得指向最后一个元素的引用v 1024; //改变c中的元素auto v2 c1.back(); //v2不…

【笔记】Dynamic Taint Analysis 动态污点分析

Dynamic Taint Analysis 动态污点分析 什么是动态污点分析&#xff1f;为什么要搞动态污点分析&#xff1f; “污点”指的是什么&#xff1f; DTA中的“污点”指代的是不可信的输入&#xff0c;比如用户输入、网络请求、文件数据等。比方说&#xff0c;如果把程序看作一个城市&…

知识图谱检索 Graph-Based Retriever:文本块到结构化数据的转换,解决语义检索捕获不了的长尾关系

知识图谱检索 Graph-Based Retriever&#xff1a;文本块到结构化数据的转换&#xff0c;解决语义检索捕获不了的长尾关系 如何理解&#xff1f;如何分析&#xff1f;升维降维梳理为什么这种解法能查到长尾内容&#xff0c;而传统语义不行&#xff1f;解法拆解 如何关联&#xf…

大觅网之综合管理(Comprehensive Management of Da Mi Network)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

MySQL InnoDB 事务commit逻辑分析

一、前言 事务提交是事务即将落盘的一系列操作&#xff0c;涉及redo\undo log的写盘、bin log的写盘、事务状态的重置、各种参数的改变、无用undo log的清理等方方面面。 在commit过程会有两个阶段&#xff1a;一个是prepare阶段&#xff0c;入口函数为trx_prepare_low&#…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-25

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-09-25 1. PromSec: Prompt Optimization for Secure Generation of Functional Source Code with Large Language Models (LLMs) M Nazzal, I Khalil, A Khreishah, NH Phan - arXiv preprint arXiv:2409.12699, 2…

无人机探测:光电侦测技术详解

无人机探测技术作为现代军事、安防及民用领域的重要组成部分&#xff0c;其核心在于高效、精准地识别与跟踪空中目标。光电侦测技术&#xff0c;作为无人机探测的主要手段之一&#xff0c;利用光学与电子学原理&#xff0c;通过捕捉并分析无人机反射或发射的光信号&#xff0c;…

Debian与Ubuntu:深入解读两大Linux发行版的历史与联系

Debian与Ubuntu&#xff1a;深入解读两大Linux发行版的历史与联系 引言 在开源操作系统的领域中&#xff0c;Debian和Ubuntu是两款备受瞩目的Linux发行版。它们不仅在技术上有着密切的联系&#xff0c;而且各自的发展历程和理念也对开源社区产生了深远的影响。本文将详细介绍…

【C++指南】C++中的内存对齐规则及原因详解

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 目录 引言 一、为什么要进行内存对齐 二、C中的内存对齐规则 三、内存对齐示例讲解 示例代码 运行…

Spring Boot 进阶-第一个程序HelloWorld

从我们学习编程语言开始,每次入门一个语言都是从Hello World开始,当然这里我们也不例外。首先从一个简单的HelloWorld程序开始。 既然是要学着做Java Web开发,那么首先需要了解的就是如何去编写一个RESTFul风格的接口,这里我们就需要引入一个pom的依赖。 <dependency&g…

矩阵分析 学习笔记4 内积与Gram矩阵

内积 定义 由于对称&#xff0c;第二变元线性那第一变元也线性了。例如这个&#xff1a;

【YashanDB知识库】yashandb执行包含带oracle dblink表的sql时性能差

本文内容来自YashanDB官网&#xff0c;具体内容请见https://www.yashandb.com/newsinfo/7396959.html?templateId1718516 问题现象 yashandb执行带oracle dblink表的sql性能差&#xff1a; 同样的语句&#xff0c;同样的数据&#xff0c;oracle通过dblink访问远端oracle执行…

spring学习 【基础】

目录 1.将bean交给spring容器管理 1. 编写bean对象 2. 在resources目录下创建spring config xml文件&#xff0c;并管理bean对象 3. spring容器管理工厂 2.Spring Bean 生命周期 2.1 Spring Bean初始化方法 2.1.1 自定义初始化方法 2.1.2 实现接口(InitializingBean)…