https://playground.babylonjs.com/#NNRIZL#20

var createScene = function () {
// This creates a basic Babylon Scene object (non-mesh)
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.ArcRotateCamera("camera", BABYLON.Tools.ToRadians(0), BABYLON.Tools.ToRadians(57.3), 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
// Default intensity is 1. Let's dim the light a small amount
light.intensity = 0.7;
// Our built-in 'sphere' shape.
var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2, segments: 32}, scene);
// Move the sphere upward 1/2 its height
sphere.position.y = 3;
// Our built-in 'ground' shape.
var ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 6, height: 6}, scene);
ground.position.y=1
const createCross = () => {
// 创建水平平面的顶点数据
const horizontalPlane = BABYLON.MeshBuilder.CreatePlane("horizontalPlane", { width: 4, height: 1 }, scene);
const horizontalVertexData = BABYLON.VertexData.ExtractFromMesh(horizontalPlane);
horizontalPlane.dispose(); // 销毁临时网格
// 创建垂直平面的顶点数据
const verticalPlane = BABYLON.MeshBuilder.CreatePlane("verticalPlane", { width: 1, height: 4 }, scene);
verticalPlane.rotation.y = Math.PI / 2; // 旋转 90 度
const verticalVertexData = BABYLON.VertexData.ExtractFromMesh(verticalPlane);
verticalPlane.dispose(); // 销毁临时网格
// 合并顶点数据
const mergedVertexData = new BABYLON.VertexData();
mergedVertexData.positions = horizontalVertexData.positions.concat(verticalVertexData.positions);
mergedVertexData.indices = horizontalVertexData.indices.concat(verticalVertexData.indices.map(index => index + horizontalVertexData.positions.length / 3));
mergedVertexData.normals = horizontalVertexData.normals.concat(verticalVertexData.normals);
mergedVertexData.uvs = horizontalVertexData.uvs.concat(verticalVertexData.uvs);
// 创建融合后的 Mesh
const cross = new BABYLON.Mesh("cross", scene);
mergedVertexData.applyToMesh(cross);
return cross;
};
const cross = createCross();
cross.position.y=-2;
cross.rotation.x = Math.PI / 2; // 旋转90度,使其垂直
const texture1 = new BABYLON.Texture("textures/speckles.jpg", scene);
const texture2 = new BABYLON.Texture("textures/floor.png", scene);
texture2.wrapU = BABYLON.Texture.WRAP_REPEAT; // 设置纹理在U方向重复
texture2.wrapV = BABYLON.Texture.WRAP_REPEAT; // 设置纹理在V方向重复
const shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, {
vertexSource: `
precision highp float;
// Attributes
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
varying vec2 vUv;
varying vec2 vUv2;
// Uniforms
uniform mat4 worldViewProjection;
// Varying
varying vec4 vPosition;
varying vec3 vNormal;
void main() {
vec4 p = vec4(position, 1.0);
vPosition = p;
vNormal = normal;
gl_Position = worldViewProjection * p;
vUv = uv; // 传递uv到Fragment Shader
vUv2 = uv * 4.0; // 缩放UV坐标,使纹理重复4次
}
`,
fragmentSource: `
precision highp float;
varying vec4 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
varying vec2 vUv2;
uniform sampler2D textureSampler1; // 纹理采样器
uniform sampler2D textureSampler2; // 纹理采样器
void main(void) {
//float edge = dot(normalize(vNormal), vec3(0.0, 0.0, 1.0));
float edgeThreshold = 0.01;
vec4 textureColor = vec4(1.0, 0.0, 0.0, 1.0);
if (vUv.x < edgeThreshold || vUv.x > 1.0 - edgeThreshold || vUv.y < edgeThreshold || vUv.y > 1.0 - edgeThreshold) {
//gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); // 边缘显示绿色
textureColor = texture2D(textureSampler2, vUv2);
} else {
//gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 非边缘显示红色
textureColor = texture2D(textureSampler1, vUv2);
}
// 定义车道线的位置和宽度
float laneCenter = 0.5; // 车道线在UV空间的中心位置(0.5表示中间)
float laneWidth = 0.005; // 车道线的宽度
float laneEdge = 0.005; // 车道线的边缘模糊宽度
// 计算车道线的强度
float laneStrength = smoothstep(laneCenter - laneWidth - laneEdge, laneCenter - laneWidth, vUv.y) -
smoothstep(laneCenter + laneWidth, laneCenter + laneWidth + laneEdge, vUv.y);
// 定义虚线参数
float dashLength = 0.2; // 每段虚线的长度
float gapLength = 0.05; // 每段间隔的长度
float dashFrequency = dashLength + gapLength; // 虚线的总周期长度
float dashPosition = mod(vUv.x, dashFrequency); // 计算当前UV坐标在周期中的位置
// 判断是否在虚线范围内
float isDash = step(dashPosition, dashLength); // 如果在虚线范围内,isDash = 1.0,否则为 0.0
// 定义车道线颜色(黄色)
vec4 laneColor = vec4(1.0, 1.0, 0.0, 1.0); // 黄色
// 混合纹理颜色和车道线颜色
vec4 finalColor = mix(textureColor, laneColor, laneStrength * isDash);
gl_FragColor = finalColor;
}
`,
}, {
attributes: ["position", "normal", "uv"],
uniforms: ["worldViewProjection"],
});
shaderMaterial.setTexture("textureSampler1", texture1);
shaderMaterial.setTexture("textureSampler2", texture2);
sphere.material = shaderMaterial;
ground.material = shaderMaterial;
cross.material = shaderMaterial;
return scene;
};