Embrace the AI Era
I am transitioning from a traditional role as a real estate Project Accountant to the frontier of AI applications, inspired by pioneers like DeepSeek. My conviction grows stronger: accessible open-source LLMs will democratize AI adoption across industries. While my background in economics and accounting has equipped me with analytical rigor, I’m now actively upskilling in AI tools and use cases — from automating financial workflows to exploring multimodal business solutions.

As a young professional, I aim to bridge legacy systems with AI-driven innovation. Let’s not just witness this revolution, but shape it.

< My Journey > Start My Journey 2018 Traditional Accounting Roots Started my career as a Project Accountant in real estate, focusing on financial analysis. 2020 First Steps into Automation Created my first Python scripts to automate Excel reporting, reducing manual work by 60%. 2022 AI Discovery & Learning Discovered AI potential in transforming financial processes and started learning. 2024 All in AI Fully transitioned into AI applications, bridging accounting expertise with AI. Traditional accounting methods import pandas as pd df = pd.read_excel(file_path) df[‘Profit’] = df[‘Revenue’] – df[‘Costs’] summary = df.groupby(‘Project’).sum() Python automation scripts Exploring neural networks All in AI Financial Analysis Document Automation Predictive Analytics AI Reporting AI integration ecosystem Animation Flow: Timeline nodes appear sequentially, triggering corresponding animations on the right

Create anything

Welcome to a world of limitless possibilities, where the journey is as exhilarating as the destination, and where every moment is an opportunity to make your mark on the canvas of existence. The only limit is the extent of your imagination.

Create anything

Welcome to a world of limitless possibilities, where the journey is as exhilarating as the destination, and where every moment is an opportunity to make your mark on the canvas of existence. The only limit is the extent of your imagination.

Create anything

Welcome to a world of limitless possibilities, where the journey is as exhilarating as the destination, and where every moment is an opportunity to make your mark on the canvas of existence. The only limit is the extent of your imagination.

Our values

Our mission is to challenge the status quo, embrace innovation, and create meaningful, lasting impact through everything we do. Driven by a vision to inspire change, push boundaries, we deliver work that truly makes a difference.

Contact

Go back

Your message has been sent

Our mission is to challenge the status quo, embrace innovation, and create meaningful, lasting impact through everything we do.

// 1. 首先添加Three.js库 document.addEventListener('DOMContentLoaded', function() { // 创建并加载Three.js脚本 var threeScript = document.createElement('script'); threeScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/three.js/0.152.2/three.min.js'; threeScript.onload = loadPostProcessing; // 当Three.js加载完成后,加载postprocessing document.head.appendChild(threeScript); // 2. 加载postprocessing库的函数 function loadPostProcessing() { var postprocessingScript = document.createElement('script'); postprocessingScript.src = 'https://cdn.jsdelivr.net/npm/postprocessing@6.32.2/build/postprocessing.min.js'; postprocessingScript.onload = initHyperspeed; // 当postprocessing加载完成后,初始化Hyperspeed document.head.appendChild(postprocessingScript); } // 3. 初始化Hyperspeed function initHyperspeed() { // Hyperspeed代码 const effectOptions = { onSpeedUp: function() { }, onSlowDown: function() { }, distortion: 'turbulentDistortion', length: 400, roadWidth: 10, islandWidth: 2, lanesPerRoad: 4, fov: 90, fovSpeedUp: 150, speedUp: 2, carLightsFade: 0.4, totalSideLightSticks: 20, lightPairsPerRoadWay: 40, shoulderLinesWidthPercentage: 0.05, brokenLinesWidthPercentage: 0.1, brokenLinesLengthPercentage: 0.5, lightStickWidth: [0.12, 0.5], lightStickHeight: [1.3, 1.7], movingAwaySpeed: [60, 80], movingCloserSpeed: [-120, -160], carLightsLength: [400 * 0.03, 400 * 0.2], carLightsRadius: [0.05, 0.14], carWidthPercentage: [0.3, 0.5], carShiftX: [-0.8, 0.8], carFloorSeparation: [0, 5], colors: { roadColor: 0x080808, islandColor: 0x0a0a0a, background: 0x000000, shoulderLines: 0xFFFFFF, brokenLines: 0xFFFFFF, leftCars: [0xD856BF, 0x6750A2, 0xC247AC], rightCars: [0x03B3C3, 0x0E5EA5, 0x324555], sticks: 0x03B3C3, } }; // 辅助函数 const random = function(base) { if (Array.isArray(base)) return Math.random() * (base[1] - base[0]) + base[0]; return Math.random() * base; }; const pickRandom = function(arr) { if (Array.isArray(arr)) return arr[Math.floor(Math.random() * arr.length)]; return arr; }; function lerp(current, target, speed, limit) { if (speed === undefined) speed = 0.1; if (limit === undefined) limit = 0.001; let change = (target - current) * speed; if (Math.abs(change) < limit) { change = target - current; } return change; } function resizeRendererToDisplaySize(renderer, setSize) { const canvas = renderer.domElement; const width = canvas.clientWidth; const height = canvas.clientHeight; const needResize = canvas.width !== width || canvas.height !== height; if (needResize) { setSize(width, height, false); } return needResize; } let nsin = function(val) { return Math.sin(val) * 0.5 + 0.5; }; // 扭曲效果定义 const mountainUniforms = { uFreq: { value: new THREE.Vector3(3, 6, 10) }, uAmp: { value: new THREE.Vector3(30, 30, 20) } }; const xyUniforms = { uFreq: { value: new THREE.Vector2(5, 2) }, uAmp: { value: new THREE.Vector2(25, 15) } }; const LongRaceUniforms = { uFreq: { value: new THREE.Vector2(2, 3) }, uAmp: { value: new THREE.Vector2(35, 10) } }; const turbulentUniforms = { uFreq: { value: new THREE.Vector4(4, 8, 8, 1) }, uAmp: { value: new THREE.Vector4(25, 5, 10, 10) } }; const deepUniforms = { uFreq: { value: new THREE.Vector2(4, 8) }, uAmp: { value: new THREE.Vector2(10, 20) }, uPowY: { value: new THREE.Vector2(20, 2) } }; const distortions = { mountainDistortion: { uniforms: mountainUniforms, getDistortion: ` uniform vec3 uAmp; uniform vec3 uFreq; #define PI 3.14159265358979 float nsin(float val){ return sin(val) * 0.5 + 0.5; } instanced.setAttribute( "aOffset", new THREE.InstancedBufferAttribute(new Float32Array(aOffset), 3, false) ); instanced.setAttribute( "aMetrics", new THREE.InstancedBufferAttribute(new Float32Array(aMetrics), 3, false) ); instanced.setAttribute( "aColor", new THREE.InstancedBufferAttribute(new Float32Array(aColor), 3, false) ); let material = new THREE.ShaderMaterial({ fragmentShader: carLightsFragment, vertexShader: carLightsVertex, transparent: true, uniforms: Object.assign( { uTime: { value: 0 }, uTravelLength: { value: options.length }, uFade: { value: this.fade } }, this.webgl.fogUniforms, options.distortion.uniforms ) }); material.onBeforeCompile = function(shader) { // 修复: 直接添加getDistortion函数到顶点着色器 shader.vertexShader = shader.vertexShader.replace( 'void main() {', options.distortion.getDistortion + 'nvoid main() {' ); }; let mesh = new THREE.Mesh(instanced, material); mesh.frustumCulled = false; this.webgl.scene.add(mesh); this.mesh = mesh; } update(time) { this.mesh.material.uniforms.uTime.value = time; } } class App { constructor(container, options) { if (options === undefined) options = {}; this.options = options; if (this.options.distortion == null) { this.options.distortion = { uniforms: distortion_uniforms, getDistortion: distortion_vertex }; } this.container = container; this.renderer = new THREE.WebGLRenderer({ antialias: false, alpha: true }); this.renderer.setSize(container.offsetWidth, container.offsetHeight, false); this.renderer.setPixelRatio(window.devicePixelRatio); this.composer = new postprocessing.EffectComposer(this.renderer); container.append(this.renderer.domElement); this.camera = new THREE.PerspectiveCamera( options.fov, container.offsetWidth / container.offsetHeight, 0.1, 10000 ); this.camera.position.z = -5; this.camera.position.y = 8; this.camera.position.x = 0; this.scene = new THREE.Scene(); this.scene.background = null; // Ensure scene background is transparent let fog = new THREE.Fog( options.colors.background, options.length * 0.2, options.length * 500 ); this.scene.fog = fog; this.fogUniforms = { fogColor: { value: fog.color }, fogNear: { value: fog.near }, fogFar: { value: fog.far } }; this.clock = new THREE.Clock(); this.assets = {}; this.disposed = false; this.road = new Road(this, options); this.leftCarLights = new CarLights( this, options, options.colors.leftCars, options.movingAwaySpeed, new THREE.Vector2(0, 1 - options.carLightsFade) ); this.rightCarLights = new CarLights( this, options, options.colors.rightCars, options.movingCloserSpeed, new THREE.Vector2(1, 0 + options.carLightsFade) ); this.leftSticks = new LightsSticks(this, options); this.fovTarget = options.fov; this.speedUpTarget = 0; } update(delta) { let lerpPercentage = Math.exp(-(-60 * Math.log2(1 - 0.1)) * delta); this.speedUp += lerp( this.speedUp, this.speedUpTarget, lerpPercentage, 0.00001 ); this.timeOffset += this.speedUp * delta; let time = this.clock.elapsedTime + this.timeOffset; this.rightCarLights.update(time); this.leftCarLights.update(time); this.leftSticks.update(time); this.road.update(time); let updateCamera = false; let fovChange = lerp(this.camera.fov, this.fovTarget, lerpPercentage); if (fovChange !== 0) { this.camera.fov += fovChange * delta * 6; updateCamera = true; } if (this.options.distortion.getJS) { const distortion = this.options.distortion.getJS(0.025, time); this.camera.lookAt( new THREE.Vector3( this.camera.position.x + distortion.x, this.camera.position.y + distortion.y, this.camera.position.z + distortion.z ) ); updateCamera = true; } if (updateCamera) { this.camera.updateProjectionMatrix(); } } render(delta) { this.composer.render(delta); } dispose() { this.disposed = true; } setSize(width, height, updateStyles) { this.composer.setSize(width, height, updateStyles); } tick() { if (this.disposed || !this) return; if (resizeRendererToDisplaySize(this.renderer, this.setSize)) { const canvas = this.renderer.domElement; this.camera.aspect = canvas.clientWidth / canvas.clientHeight; this.camera.updateProjectionMatrix(); } const delta = this.clock.getDelta(); this.render(delta); this.update(delta); requestAnimationFrame(this.tick); } } this.speedUp = 0; this.timeOffset = 0; this.tick = this.tick.bind(this); this.init = this.init.bind(this); this.setSize = this.setSize.bind(this); this.onMouseDown = this.onMouseDown.bind(this); this.onMouseUp = this.onMouseUp.bind(this); } initPasses() { this.renderPass = new postprocessing.RenderPass(this.scene, this.camera); this.bloomPass = new postprocessing.EffectPass( this.camera, new postprocessing.BloomEffect({ luminanceThreshold: 0.2, luminanceSmoothing: 0, resolutionScale: 1 }) ); const smaaPass = new postprocessing.EffectPass( this.camera, new postprocessing.SMAAEffect({ preset: postprocessing.SMAAPreset.MEDIUM, searchImage: postprocessing.SMAAEffect.searchImageDataURL, areaImage: postprocessing.SMAAEffect.areaImageDataURL }) ); this.renderPass.renderToScreen = false; this.bloomPass.renderToScreen = false; smaaPass.renderToScreen = true; this.composer.addPass(this.renderPass); this.composer.addPass(this.bloomPass); this.composer.addPass(smaaPass); } loadAssets() { const assets = this.assets; return new Promise(function(resolve) { const manager = new THREE.LoadingManager(resolve); const searchImage = new Image(); const areaImage = new Image(); assets.smaa = {}; searchImage.addEventListener("load", function() { assets.smaa.search = this; manager.itemEnd("smaa-search"); }); areaImage.addEventListener("load", function() { assets.smaa.area = this; manager.itemEnd("smaa-area"); }); manager.itemStart("smaa-search"); manager.itemStart("smaa-area"); searchImage.src = postprocessing.SMAAEffect.searchImageDataURL; areaImage.src = postprocessing.SMAAEffect.areaImageDataURL; }); } init() { this.initPasses(); const options = this.options; this.road.init(); this.leftCarLights.init(); this.leftCarLights.mesh.position.setX( -options.roadWidth / 2 - options.islandWidth / 2 ); this.rightCarLights.init(); this.rightCarLights.mesh.position.setX( options.roadWidth / 2 + options.islandWidth / 2 ); this.leftSticks.init(); this.leftSticks.mesh.position.setX( -(options.roadWidth + options.islandWidth / 2) ); this.container.addEventListener("mousedown", this.onMouseDown); this.container.addEventListener("mouseup", this.onMouseUp); this.container.addEventListener("mouseout", this.onMouseUp); this.tick(); } onMouseDown(ev) { if (this.options.onSpeedUp) this.options.onSpeedUp(ev); this.fovTarget = this.options.fovSpeedUp; this.speedUpTarget = this.options.speedUp; } onMouseUp(ev) { if (this.options.onSlowDown) this.options.onSlowDown(ev); this.fovTarget = this.options.fov; this.speedUpTarget = 0; } update(time) { this.mesh.material.uniforms.uTime.value = time; } } class LightsSticks { constructor(webgl, options) { this.webgl = webgl; this.options = options; } init() { const options = this.options; const geometry = new THREE.PlaneGeometry(1, 1); let instanced = new THREE.InstancedBufferGeometry().copy(geometry); let totalSticks = options.totalSideLightSticks; instanced.instanceCount = totalSticks; let stickoffset = options.length / (totalSticks - 1); const aOffset = []; const aColor = []; const aMetrics = []; let colors = options.colors.sticks; if (Array.isArray(colors)) { colors = colors.map(function(c) { return new THREE.Color(c); }); } else { colors = new THREE.Color(colors); } for (let i = 0; i < totalSticks; i++) { let width = random(options.lightStickWidth); let height = random(options.lightStickHeight); aOffset.push((i - 1) * stickoffset * 2 + stickoffset * Math.random()); let color = pickRandom(colors); aColor.push(color.r); aColor.push(color.g); aColor.push(color.b); aMetrics.push(width); aMetrics.push(height); } instanced.setAttribute( "aOffset", new THREE.InstancedBufferAttribute(new Float32Array(aOffset), 1, false) ); instanced.setAttribute( "aColor", new THREE.InstancedBufferAttribute(new Float32Array(aColor), 3, false) ); instanced.setAttribute( "aMetrics", new THREE.InstancedBufferAttribute(new Float32Array(aMetrics), 2, false) ); const material = new THREE.ShaderMaterial({ fragmentShader: sideSticksFragment, vertexShader: sideSticksVertex, side: THREE.DoubleSide, uniforms: Object.assign( { uTravelLength: { value: options.length }, uTime: { value: 0 } }, this.webgl.fogUniforms, options.distortion.uniforms ) }); material.onBeforeCompile = function(shader) { // 修复: 直接添加getDistortion函数到顶点着色器 shader.vertexShader = shader.vertexShader.replace( 'void main(){', options.distortion.getDistortion + 'nvoid main(){' ); }; const mesh = new THREE.Mesh(instanced, material); mesh.frustumCulled = false; this.webgl.scene.add(mesh); this.mesh = mesh; vec3 getDistortion(float progress){ float movementProgressFix = 0.02; return vec3( cos(progress * PI * uFreq.x + uTime) * uAmp.x - cos(movementProgressFix * PI * uFreq.x + uTime) * uAmp.x, nsin(progress * PI * uFreq.y + uTime) * uAmp.y - nsin(movementProgressFix * PI * uFreq.y + uTime) * uAmp.y, nsin(progress * PI * uFreq.z + uTime) * uAmp.z - nsin(movementProgressFix * PI * uFreq.z + uTime) * uAmp.z ); } `, getJS: function(progress, time) { let movementProgressFix = 0.02; let uFreq = mountainUniforms.uFreq.value; let uAmp = mountainUniforms.uAmp.value; let distortion = new THREE.Vector3( Math.cos(progress * Math.PI * uFreq.x + time) * uAmp.x - Math.cos(movementProgressFix * Math.PI * uFreq.x + time) * uAmp.x, nsin(progress * Math.PI * uFreq.y + time) * uAmp.y - nsin(movementProgressFix * Math.PI * uFreq.y + time) * uAmp.y, nsin(progress * Math.PI * uFreq.z + time) * uAmp.z - nsin(movementProgressFix * Math.PI * uFreq.z + time) * uAmp.z ); let lookAtAmp = new THREE.Vector3(2, 2, 2); let lookAtOffset = new THREE.Vector3(0, 0, -5); return distortion.multiply(lookAtAmp).add(lookAtOffset); } }, xyDistortion: { uniforms: xyUniforms, getDistortion: ` uniform vec2 uFreq; uniform vec2 uAmp; #define PI 3.14159265358979 vec3 getDistortion(float progress){ float movementProgressFix = 0.02; return vec3( cos(progress * PI * uFreq.x + uTime) * uAmp.x - cos(movementProgressFix * PI * uFreq.x + uTime) * uAmp.x, sin(progress * PI * uFreq.y + PI/2. + uTime) * uAmp.y - sin(movementProgressFix * PI * uFreq.y + PI/2. + uTime) * uAmp.y, 0. ); } `, getJS: function(progress, time) { let movementProgressFix = 0.02; let uFreq = xyUniforms.uFreq.value; let uAmp = xyUniforms.uAmp.value; let distortion = new THREE.Vector3( Math.cos(progress * Math.PI * uFreq.x + time) * uAmp.x - Math.cos(movementProgressFix * Math.PI * uFreq.x + time) * uAmp.x, Math.sin(progress * Math.PI * uFreq.y + time + Math.PI / 2) * uAmp.y - Math.sin(movementProgressFix * Math.PI * uFreq.y + time + Math.PI / 2) * uAmp.y, 0 ); let lookAtAmp = new THREE.Vector3(2, 0.4, 1); let lookAtOffset = new THREE.Vector3(0, 0, -3); return distortion.multiply(lookAtAmp).add(lookAtOffset); } }, LongRaceDistortion: { uniforms: LongRaceUniforms, getDistortion: ` uniform vec2 uFreq; uniform vec2 uAmp; #define PI 3.14159265358979 vec3 getDistortion(float progress){ float camProgress = 0.0125; return vec3( sin(progress * PI * uFreq.x + uTime) * uAmp.x - sin(camProgress * PI * uFreq.x + uTime) * uAmp.x, sin(progress * PI * uFreq.y + uTime) * uAmp.y - sin(camProgress * PI * uFreq.y + uTime) * uAmp.y, 0. ); } `, getJS: function(progress, time) { let camProgress = 0.0125; let uFreq = LongRaceUniforms.uFreq.value; let uAmp = LongRaceUniforms.uAmp.value; let distortion = new THREE.Vector3( Math.sin(progress * Math.PI * uFreq.x + time) * uAmp.x - Math.sin(camProgress * Math.PI * uFreq.x + time) * uAmp.x, Math.sin(progress * Math.PI * uFreq.y + time) * uAmp.y - Math.sin(camProgress * Math.PI * uFreq.y + time) * uAmp.y, 0 ); let lookAtAmp = new THREE.Vector3(1, 1, 0); let lookAtOffset = new THREE.Vector3(0, 0, -5); return distortion.multiply(lookAtAmp).add(lookAtOffset); } }, turbulentDistortion: { uniforms: turbulentUniforms, getDistortion: ` uniform vec4 uFreq; uniform vec4 uAmp; float nsin(float val){ return sin(val) * 0.5 + 0.5; } #define PI 3.14159265358979 float getDistortionX(float progress){ return ( cos(PI * progress * uFreq.r + uTime) * uAmp.r + pow(cos(PI * progress * uFreq.g + uTime * (uFreq.g / uFreq.r)), 2. ) * uAmp.g ); } float getDistortionY(float progress){ return ( -nsin(PI * progress * uFreq.b + uTime) * uAmp.b + -pow(nsin(PI * progress * uFreq.a + uTime / (uFreq.b / uFreq.a)), 5.) * uAmp.a ); } vec3 getDistortion(float progress){ return vec3( getDistortionX(progress) - getDistortionX(0.0125), getDistortionY(progress) - getDistortionY(0.0125), 0. ); } `, getJS: function(progress, time) { const uFreq = turbulentUniforms.uFreq.value; const uAmp = turbulentUniforms.uAmp.value; const getX = function(p) { return Math.cos(Math.PI * p * uFreq.x + time) * uAmp.x + Math.pow(Math.cos(Math.PI * p * uFreq.y + time * (uFreq.y / uFreq.x)), 2) * uAmp.y; }; const getY = function(p) { return -nsin(Math.PI * p * uFreq.z + time) * uAmp.z - Math.pow(nsin(Math.PI * p * uFreq.w + time / (uFreq.z / uFreq.w)), 5) * uAmp.w; }; let distortion = new THREE.Vector3( getX(progress) - getX(progress + 0.007), getY(progress) - getY(progress + 0.007), 0 ); let lookAtAmp = new THREE.Vector3(-2, -5, 0); let lookAtOffset = new THREE.Vector3(0, 0, -10); return distortion.multiply(lookAtAmp).add(lookAtOffset); } }, deepDistortion: { uniforms: deepUniforms, getDistortion: ` uniform vec4 uFreq; uniform vec4 uAmp; uniform vec2 uPowY; float nsin(float val){ return sin(val) * 0.5 + 0.5; } #define PI 3.14159265358979 float getDistortionX(float progress){ return ( sin(progress * PI * uFreq.x + uTime) * uAmp.x ); } float getDistortionY(float progress){ return ( pow(abs(progress * uPowY.x), uPowY.y) + sin(progress * PI * uFreq.y + uTime) * uAmp.y ); } vec3 getDistortion(float progress){ return vec3( getDistortionX(progress) - getDistortionX(0.02), getDistortionY(progress) - getDistortionY(0.02), 0. ); } `, getJS: function(progress, time) { const uFreq = deepUniforms.uFreq.value; const uAmp = deepUniforms.uAmp.value; const uPowY = deepUniforms.uPowY.value; const getX = function(p) { return Math.sin(p * Math.PI * uFreq.x + time) * uAmp.x; }; const getY = function(p) { return Math.pow(p * uPowY.x, uPowY.y) + Math.sin(p * Math.PI * uFreq.y + time) * uAmp.y; }; let distortion = new THREE.Vector3( getX(progress) - getX(progress + 0.01), getY(progress) - getY(progress + 0.01), 0 ); let lookAtAmp = new THREE.Vector3(-2, -4, 0); let lookAtOffset = new THREE.Vector3(0, 0, -10); return distortion.multiply(lookAtAmp).add(lookAtOffset); } } }; // 定义基础distortion const distortion_uniforms = { uDistortionX: { value: new THREE.Vector2(80, 3) }, uDistortionY: { value: new THREE.Vector2(-40, 2.5) } }; const distortion_vertex = ` #define PI 3.14159265358979 uniform vec2 uDistortionX; uniform vec2 uDistortionY; float nsin(float val){ return sin(val) * 0.5 + 0.5; } vec3 getDistortion(float progress){ progress = clamp(progress, 0., 1.); float xAmp = uDistortionX.r; float xFreq = uDistortionX.g; float yAmp = uDistortionY.r; float yFreq = uDistortionY.g; return vec3( xAmp * nsin(progress * PI * xFreq - PI / 2.), yAmp * nsin(progress * PI * yFreq - PI / 2.), 0. ); } `; // Shader变量 const roadmarkings_vars = ` uniform float uLanes; uniform vec3 uBrokenLinesColor; uniform vec3 uShoulderLinesColor; uniform float uShoulderLinesWidthPercentage; uniform float uBrokenLinesWidthPercentage; uniform float uBrokenLinesLengthPercentage; highp float random(vec2 co) { highp float a = 12.9898; highp float b = 78.233; highp float c = 43758.5453; highp float dt = dot(co.xy, vec2(a, b)); highp float sn = mod(dt, 3.14); return fract(sin(sn) * c); } `; // Shader片段 const roadmarkings_fragment = ` uv.y = mod(uv.y + uTime * 0.05, 1.); // Adjust speed of markings float laneWidth = 1.0 / uLanes; float brokenLineWidth = laneWidth * uBrokenLinesWidthPercentage; float laneEmptySpace = 1. - uBrokenLinesLengthPercentage; float brokenLines = step(1.0 - brokenLineWidth, fract(uv.x * 2.0)) * step(laneEmptySpace, fract(uv.y * 10.0)); // Dashes in the middle float sideLines = step(1.0 - brokenLineWidth, fract((uv.x - laneWidth * (uLanes - 1.0)) * 2.0)) + step(brokenLineWidth, uv.x); // Side continuous lines brokenLines = mix(brokenLines, sideLines, uv.x); // color = mix(color, uBrokenLinesColor, brokenLines); // vec2 noiseFreq = vec2(4., 7000.); // float roadNoise = random(floor(uv * noiseFreq) / noiseFreq) * 0.02 - 0.01; // color += roadNoise; `; // 修复: 不使用include系统,改用直接替换完整的着色器代码 // 岛屿片段着色器 const islandFragment = ` #define USE_FOG; varying vec2 vUv; uniform vec3 uColor; uniform float uTime; ${THREE.ShaderChunk["fog_pars_fragment"]} void main() { vec2 uv = vUv; vec3 color = vec3(uColor); gl_FragColor = vec4(color, 1.); ${THREE.ShaderChunk["fog_fragment"]} } `; // 道路片段着色器 const roadFragment = ` #define USE_FOG; varying vec2 vUv; uniform vec3 uColor; uniform float uTime; ${roadmarkings_vars} ${THREE.ShaderChunk["fog_pars_fragment"]} void main() { vec2 uv = vUv; vec3 color = vec3(uColor); ${roadmarkings_fragment} gl_FragColor = vec4(color, 1.); ${THREE.ShaderChunk["fog_fragment"]} } `; // 道路顶点着色器 const roadVertex = ` #define USE_FOG; uniform float uTime; ${THREE.ShaderChunk["fog_pars_vertex"]} uniform float uTravelLength; varying vec2 vUv; void main() { vec3 transformed = position.xyz; vec3 distortion = getDistortion((transformed.y + uTravelLength / 2.) / uTravelLength); transformed.x += distortion.x; transformed.z += distortion.y; transformed.y += -1. * distortion.z; vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.); gl_Position = projectionMatrix * mvPosition; vUv = uv; ${THREE.ShaderChunk["fog_vertex"]} } `; // 车灯片段着色器 const carLightsFragment = ` #define USE_FOG; ${THREE.ShaderChunk["fog_pars_fragment"]} varying vec3 vColor; varying vec2 vUv; uniform vec2 uFade; void main() { vec3 color = vec3(vColor); float alpha = smoothstep(uFade.x, uFade.y, vUv.x); gl_FragColor = vec4(color, alpha); if (gl_FragColor.a < 0.0001) discard; ${THREE.ShaderChunk["fog_fragment"]} } `; // 车灯顶点着色器 const carLightsVertex = ` #define USE_FOG; ${THREE.ShaderChunk["fog_pars_vertex"]} attribute vec3 aOffset; attribute vec3 aMetrics; attribute vec3 aColor; uniform float uTravelLength; uniform float uTime; varying vec2 vUv; varying vec3 vColor; void main() { vec3 transformed = position.xyz; float radius = aMetrics.r; float myLength = aMetrics.g; float speed = aMetrics.b; transformed.xy *= radius; transformed.z *= myLength; transformed.z += myLength - mod(uTime * speed + aOffset.z, uTravelLength); transformed.xy += aOffset.xy; float progress = abs(transformed.z / uTravelLength); transformed.xyz += getDistortion(progress); vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.); gl_Position = projectionMatrix * mvPosition; vUv = uv; vColor = aColor; ${THREE.ShaderChunk["fog_vertex"]} } `; // 路灯柱顶点着色器 const sideSticksVertex = ` #define USE_FOG; ${THREE.ShaderChunk["fog_pars_vertex"]} attribute float aOffset; attribute vec3 aColor; attribute vec2 aMetrics; uniform float uTravelLength; uniform float uTime; varying vec3 vColor; mat4 rotationY( in float angle ) { return mat4( cos(angle), 0, sin(angle), 0, 0, 1.0, 0, 0, -sin(angle), 0, cos(angle), 0, 0, 0, 0, 1); } void main(){ vec3 transformed = position.xyz; float width = aMetrics.x; float height = aMetrics.y; transformed.xy *= vec2(width, height); float time = mod(uTime * 60. * 2. + aOffset, uTravelLength); transformed = (rotationY(3.14/2.) * vec4(transformed,1.)).xyz; transformed.z += - uTravelLength + time; float progress = abs(transformed.z / uTravelLength); transformed.xyz += getDistortion(progress); transformed.y += height / 2.; transformed.x += -width / 2.; vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.); gl_Position = projectionMatrix * mvPosition; vColor = aColor; ${THREE.ShaderChunk["fog_vertex"]} } `; // 路灯柱片段着色器 const sideSticksFragment = ` #define USE_FOG; ${THREE.ShaderChunk["fog_pars_fragment"]} varying vec3 vColor; void main(){ vec3 color = vec3(vColor); gl_FragColor = vec4(color,1.); ${THREE.ShaderChunk["fog_fragment"]} } `; // 类定义 class Road { constructor(webgl, options) { this.webgl = webgl; this.options = options; this.uTime = { value: 0 }; } createPlane(side, width, isRoad) { const options = this.options; let segments = 100; const geometry = new THREE.PlaneGeometry( isRoad ? options.roadWidth : options.islandWidth, options.length, 20, segments ); let uniforms = { uTravelLength: { value: options.length }, uColor: { value: new THREE.Color(isRoad ? options.colors.roadColor : options.colors.islandColor) }, uTime: this.uTime }; if (isRoad) { uniforms = Object.assign(uniforms, { uLanes: { value: options.lanesPerRoad }, uBrokenLinesColor: { value: new THREE.Color(options.colors.brokenLines) }, uShoulderLinesColor: { value: new THREE.Color(options.colors.shoulderLines) }, uShoulderLinesWidthPercentage: { value: options.shoulderLinesWidthPercentage }, uBrokenLinesLengthPercentage: { value: options.brokenLinesLengthPercentage }, uBrokenLinesWidthPercentage: { value: options.brokenLinesWidthPercentage } }); } const material = new THREE.ShaderMaterial({ fragmentShader: isRoad ? roadFragment : islandFragment, vertexShader: roadVertex, side: THREE.DoubleSide, uniforms: Object.assign( uniforms, this.webgl.fogUniforms, options.distortion.uniforms ) }); // 修复: 不再使用标签替换,而是在定义着色器时直接包含distortion逻辑 material.onBeforeCompile = function(shader) { // 添加getDistortion函数到顶点着色器 shader.vertexShader = shader.vertexShader.replace( 'void main() {', options.distortion.getDistortion + 'nvoid main() {' ); }; const mesh = new THREE.Mesh(geometry, material); mesh.rotation.x = -Math.PI / 2; mesh.position.z = -options.length / 2; mesh.position.x += (this.options.islandWidth / 2 + options.roadWidth / 2) * side; this.webgl.scene.add(mesh); return mesh; } init() { this.leftRoadWay = this.createPlane(-1, this.options.roadWidth, true); this.rightRoadWay = this.createPlane(1, this.options.roadWidth, true); this.island = this.createPlane(0, this.options.islandWidth, false); } update(time) { this.uTime.value = time; } } class CarLights { constructor(webgl, options, colors, speed, fade) { this.webgl = webgl; this.options = options; this.colors = colors; this.speed = speed; this.fade = fade; } init() { const options = this.options; let curve = new THREE.LineCurve3( new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -1) ); let geometry = new THREE.TubeGeometry(curve, 40, 1, 8, false); let instanced = new THREE.InstancedBufferGeometry().copy(geometry); instanced.instanceCount = options.lightPairsPerRoadWay * 2; let laneWidth = options.roadWidth / options.lanesPerRoad; let aOffset = []; let aMetrics = []; let aColor = []; let colors = this.colors; if (Array.isArray(colors)) { colors = colors.map(function(c) { return new THREE.Color(c); }); } else { colors = new THREE.Color(colors); } for (let i = 0; i < options.lightPairsPerRoadWay; i++) { let radius = random(options.carLightsRadius); let length = random(options.carLightsLength); let speed = random(this.speed); let carLane = i % options.lanesPerRoad; // Fix lane assignment to spread across lanes let laneX = carLane * laneWidth - options.roadWidth / 2 + laneWidth / 2; let carWidth = random(options.carWidthPercentage) * laneWidth; let carShiftX = random(options.carShiftX) * laneWidth; laneX += carShiftX; let offsetY = random(options.carFloorSeparation) + radius * 1.3; let offsetZ = -random(options.length); aOffset.push(laneX - carWidth / 2); aOffset.push(offsetY); aOffset.push(offsetZ); aOffset.push(laneX + carWidth / 2); aOffset.push(offsetY); aOffset.push(offsetZ); aMetrics.push(radius); aMetrics.push(length); aMetrics.push(speed); aMetrics.push(radius); aMetrics.push(length); aMetrics.push(speed); let color = pickRandom(colors); aColor.push(color.r); aColor.push(color.g); aColor.push(color.b); aColor.push(color.r); aColor.push(color.g); aColor.push(color.b); }