From c3549f927818fe6286932442577a4672aa6dd42a Mon Sep 17 00:00:00 2001
From: nicomacbookpro <805879871@qq.com>
Date: Thu, 21 Aug 2025 09:29:02 +0800
Subject: [PATCH] =?UTF-8?q?=E8=8D=89=E5=9C=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/utils/plane_grass.js | 161 ++++++
src/views/bigScreen/home/index copy.vue | 638 ------------------------
src/views/bigScreen/home/index.vue | 8 +
3 files changed, 169 insertions(+), 638 deletions(-)
create mode 100644 src/utils/plane_grass.js
delete mode 100644 src/views/bigScreen/home/index copy.vue
diff --git a/src/utils/plane_grass.js b/src/utils/plane_grass.js
new file mode 100644
index 0000000..0fe686e
--- /dev/null
+++ b/src/utils/plane_grass.js
@@ -0,0 +1,161 @@
+import * as THREE from "three";
+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 x = surfaceMin + Math.random() * size
+ const y = surfaceMin + Math.random() * size
+
+ 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()
+ }
+ 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
+ ]
+ }
+ }
+}
+
+class Grass extends THREE.Mesh {
+ constructor(size, count) {
+ const geometry = new GrassGeometry(size, count)
+ const material = new THREE.ShaderMaterial({
+ uniforms: {
+ uCloud: { value: "" },
+ 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.PlaneGeometry(50, 50).rotateX(Math.PI / 2),
+ material
+ )
+ floor.position.y = -Number.EPSILON
+ this.add(floor)
+
+ }
+ update(time) {
+ this.material.uniforms.uTime.value = time
+ }
+}
+
+const planeGrass = new Grass(50, 100000)
+export { planeGrass }
diff --git a/src/views/bigScreen/home/index copy.vue b/src/views/bigScreen/home/index copy.vue
deleted file mode 100644
index 760ddac..0000000
--- a/src/views/bigScreen/home/index copy.vue
+++ /dev/null
@@ -1,638 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/views/bigScreen/home/index.vue b/src/views/bigScreen/home/index.vue
index 68c35d9..c5a9d02 100644
--- a/src/views/bigScreen/home/index.vue
+++ b/src/views/bigScreen/home/index.vue
@@ -55,6 +55,7 @@ import { lastData } from "@/api/bigScreen/api.js";
import { drawAxes } from "@/utils/three.js";
import { connectWebsocket, closeWebsocket } from "@/utils/websocket.js";
+import { planeGrass } from "@/utils/plane_grass.js";
export default {
name: 'Home',
props: {
@@ -259,6 +260,13 @@ export default {
this.onBallClick(falg, val)
})
this.legendList = this.demo1.getGroups()
+ planeGrass.position.set(25, 1, 25)
+ this.demo1.scene.add(planeGrass)
+ function animate(time) {
+ requestAnimationFrame(animate)
+ planeGrass.update(time)
+ }
+ animate()