第42节:自定义渲染管线:修改Three.js默认流程
概述
Three.js默认的渲染流程虽然强大,但在某些高级应用场景下可能无法满足需求。本节将深入探讨如何自定义渲染管线,覆盖内置着色器、修改渲染顺序,实现完全可控的渲染流程。
自定义渲染管线架构:
核心原理
渲染管线阶段
| 阶段 | 默认行为 | 自定义能力 |
|---|---|---|
| 场景遍历 | 自动排序和批处理 | 自定义遍历顺序 |
| 着色器编译 | 自动生成着色器代码 | 替换或修改着色器 |
| 渲染状态 | 自动状态管理 | 手动状态控制 |
| 后期处理 | 可选后处理通道 | 完全自定义后处理链 |
着色器修改技术
// 着色器替换策略
class ShaderReplacement {
static replaceMaterialShaders(originalMaterial, customShader) {
// 保存原始着色器
originalMaterial.userData.originalShader = {
vertexShader: originalMaterial.vertexShader,
fragmentShader: originalMaterial.fragmentShader,
uniforms: THREE.UniformsUtils.clone(originalMaterial.uniforms)
};
// 应用自定义着色器
originalMaterial.vertexShader = customShader.vertexShader;
originalMaterial.fragmentShader = customShader.fragmentShader;
originalMaterial.needsUpdate = true;
}
static restoreMaterialShaders(material) {
if (material.userData.originalShader) {
material.vertexShader = material.userData.originalShader.vertexShader;
material.fragmentShader = material.userData.originalShader.fragmentShader;
material.uniforms = material.userData.originalShader.uniforms;
material.needsUpdate = true;
}
}
}
完整代码实现
自定义渲染器系统
<template>
<div class="custom-renderer-container">
<!-- 渲染视图 -->
<div class="render-view">
<canvas ref="renderCanvas" class="render-canvas"></canvas>
<!-- 调试信息 -->
<div class="debug-info">
<div class="info-item">
<span>渲染模式:</span>
<span>{{ currentRenderMode }}</span>
</div>
<div class="info-item">
<span>绘制调用:</span>
<span>{{ drawCalls }}</span>
</div>
<div class="info-item">
<span>三角形数量:</span>
<span>{{ triangleCount.toLocaleString() }}</span>
</div>
<div class="info-item">
<span>帧率:</span>
<span>{{ fps }} FPS</span>
</div>
</div>
</div>
<!-- 控制面板 -->
<div class="control-panel">
<div class="panel-section">
<h3>🎨 渲染模式</h3>
<div class="mode-buttons">
<button v-for="mode in renderModes"
:key="mode.id"
:class="{ active: renderMode === mode.id }"
@click="setRenderMode(mode.id)"
class="mode-button">
{{ mode.name }}
</button>
</div>
</div>
<div class="panel-section">
<h3>⚙️ 着色器控制</h3>
<div class="shader-controls">
<div class="control-group">
<label>顶点着色器修改</label>
<select v-model="vertexModification">
<option value="none">无修改</option>
<option value="wave">波浪效果</option>
<option value="displacement">顶点位移</option>
<option value="morph">形变动画</option>
</select>
</div>
<div class="control-group">
<label>片段着色器修改</label>
<select v-model="fragmentModification">
<option value="none">无修改</option>
<option value="outline">轮廓描边</option>
<option value="toon">卡通渲染</option>
<option value="poster">海报化</option>
</select>
</div>
<div class="control-group">
<label>自定义Uniforms</label>
<div class="uniform-controls">
<div class="uniform-item">
<span>时间:</span>
<input type="range" v-model="timeUniform" min="0" max="10" step="0.1">
</div>
<div class="uniform-item">
<span>强度:</span>
<input type="range" v-model="intensityUniform" min="0" max="2" step="0.1">
</div>
</div>
</div>
</div>
</div>
<div class="panel-section">
<h3>🔧 渲染设置</h3>
<div class="render-settings">
<div class="setting-group">
<label>
<input type="checkbox" v-model="enableCustomSorting">
自定义渲染排序
</label>
</div>
<div class="setting-group">
<label>
<input type="checkbox" v-model="enableMultiPass">
多通道渲染
</label>
</div>
<div class="setting-group">
<label>渲染顺序</label>
<select v-model="renderOrder">
<option value="frontToBack">前到后</option>
<option value="backToFront">后到前</option>
<option value="material">按材质</option>
</select>
</div>
</div>
</div>
<div class="panel-section">
<h3>📊 性能监控</h3>
<div class="performance-stats">
<div class="stat-item">
<span>编译着色器:</span>
<span>{{ ***piledShaders }}</span>
</div>
<div class="stat-item">
<span>渲染时间:</span>
<span>{{ renderTime }}ms</span>
</div>
<div class="stat-item">
<span>GPU内存:</span>
<span>{{ gpuMemory }} MB</span>
</div>
</div>
</div>
</div>
<!-- 着色器编辑器 -->
<div v-if="showShaderEditor" class="shader-editor-modal">
<div class="editor-content">
<div class="editor-header">
<h3>着色器编辑器</h3>
<button @click="showShaderEditor = false" class="close-button">×</button>
</div>
<div class="editor-body">
<div class="editor-tabs">
<button :class="{ active: activeShaderTab === 'vertex' }"
@click="activeShaderTab = 'vertex'">
顶点着色器
</button>
<button :class="{ active: activeShaderTab === 'fragment' }"
@click="activeShaderTab = 'fragment'">
片段着色器
</button>
</div>
<textarea v-model="shaderCode[activeShaderTab]"
class="shader-textarea"
spellcheck="false"></textarea>
<div class="editor-actions">
<button @click="applyShaderCode" class="action-button">应用修改</button>
<button @click="resetShaderCode" class="action-button secondary">重置</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { onMounted, onUnmounted, ref, reactive, ***puted } from 'vue';
import * as THREE from 'three';
// 自定义渲染器
class CustomWebGLRenderer extends THREE.WebGLRenderer {
constructor(parameters = {}) {
super(parameters);
this.customRenderCallbacks = [];
this.renderOverride = null;
this.sortObjectsFunction = null;
}
// 重写渲染方法
render(scene, camera) {
const startTime = performance.now();
if (this.renderOverride) {
this.renderOverride(scene, camera, this);
} else {
this.customRender(scene, camera);
}
// 执行自定义回调
this.customRenderCallbacks.forEach(callback => {
callback(scene, camera, this);
});
this.lastRenderTime = performance.now() - startTime;
}
// 自定义渲染流程
customRender(scene, camera) {
// 自定义场景遍历和排序
const renderList = this.getCustomRenderList(scene);
// 设置渲染状态
this.setCustomRenderState();
// 执行渲染
this.renderCustomList(renderList, camera);
}
// 获取自定义渲染列表
getCustomRenderList(scene) {
const renderList = [];
scene.traverse(object => {
if (object.isMesh || object.isSprite || object.isLine) {
renderList.push(object);
}
});
// 自定义排序
if (this.sortObjectsFunction) {
renderList.sort(this.sortObjectsFunction);
}
return renderList;
}
// 设置自定义渲染状态
setCustomRenderState() {
const gl = this.getContext();
// 自定义GL状态
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.BACK);
}
// 渲染自定义列表
renderCustomList(renderList, camera) {
const gl = this.getContext();
// 清除缓冲区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 渲染每个对象
renderList.forEach(object => {
this.renderObject(object, camera);
});
}
// 渲染单个对象
renderObject(object, camera) {
if (!object.visible) return;
const material = object.material;
if (!material || material.visible === false) return;
// 应用自定义材质
this.setCustomMaterial(material);
// 渲染对象
this.renderObjectDirect(object, camera);
}
// 设置自定义材质
setCustomMaterial(material) {
if (material.isShaderMaterial || material.isRawShaderMaterial) {
// 已经是自定义材质,无需处理
return;
}
// 这里可以修改内置材质的着色器
if (material.userData.customShader) {
this.applyCustomShader(material);
}
}
// 应用自定义着色器
applyCustomShader(material) {
const customShader = material.userData.customShader;
if (!material._originalOnBefore***pile) {
material._originalOnBefore***pile = material.onBefore***pile;
}
material.onBefore***pile = (shader, renderer) => {
if (material._originalOnBefore***pile) {
material._originalOnBefore***pile(shader, renderer);
}
// 修改着色器代码
if (customShader.vertexShader) {
shader.vertexShader = this.injectShaderCode(
shader.vertexShader,
customShader.vertexShader
);
}
if (customShader.fragmentShader) {
shader.fragmentShader = this.injectShaderCode(
shader.fragmentShader,
customShader.fragmentShader
);
}
// 添加自定义uniforms
Object.assign(shader.uniforms, customShader.uniforms);
};
material.needsUpdate = true;
}
// 注入着色器代码
injectShaderCode(originalShader, customCode) {
// 在main函数前插入自定义代码
return originalShader.replace(
/void main\(\) {/,
`${customCode}\nvoid main() {`
);
}
// 添加渲染回调
addRenderCallback(callback) {
this.customRenderCallbacks.push(callback);
}
// 移除渲染回调
removeRenderCallback(callback) {
const index = this.customRenderCallbacks.indexOf(callback);
if (index > -1) {
this.customRenderCallbacks.splice(index, 1);
}
}
// 设置渲染覆盖
setRenderOverride(renderFunction) {
this.renderOverride = renderFunction;
}
// 设置排序函数
setSortFunction(sortFunction) {
this.sortObjectsFunction = sortFunction;
}
}
// 着色器管理器
class ShaderManager {
constructor() {
this.shaderTemplates = new Map();
this.initShaderTemplates();
}
initShaderTemplates() {
// 波浪效果顶点着色器
this.shaderTemplates.set('wave_vertex', `
uniform float time;
uniform float waveIntensity;
vec3 applyWaveEffect(vec3 position) {
float wave = sin(position.x * 5.0 + time) * waveIntensity;
position.y += wave;
return position;
}
`);
// 轮廓描边片段着色器
this.shaderTemplates.set('outline_fragment', `
uniform vec3 outlineColor;
uniform float outli***hreshold;
vec4 applyOutline(vec4 originalColor, vec3 normal, vec3 viewDir) {
float edge = dot(normal, viewDir);
if (edge < outli***hreshold) {
return vec4(outlineColor, 1.0);
}
return originalColor;
}
`);
// 卡通渲染片段着色器
this.shaderTemplates.set('toon_fragment', `
uniform int toonLevels;
vec4 applyToonShading(vec4 originalColor, float diffuse) {
float toon = floor(diffuse * float(toonLevels)) / float(toonLevels);
return vec4(originalColor.rgb * toon, originalColor.a);
}
`);
}
getShaderTemplate(name) {
return this.shaderTemplates.get(name) || '';
}
createCustomShader(vertexMod, fragmentMod, uniforms = {}) {
return {
vertexShader: this.getShaderTemplate(vertexMod),
fragmentShader: this.getShaderTemplate(fragmentMod),
uniforms: uniforms
};
}
}
export default {
name: 'CustomRenderer',
setup() {
// 响应式状态
const renderCanvas = ref(null);
const renderMode = ref('standard');
const vertexModification = ref('none');
const fragmentModification = ref('none');
const timeUniform = ref(0);
const intensityUniform = ref(1);
const enableCustomSorting = ref(false);
const enableMultiPass = ref(false);
const renderOrder = ref('frontToBack');
const showShaderEditor = ref(false);
const activeShaderTab = ref('vertex');
// 性能统计
const drawCalls = ref(0);
const triangleCount = ref(0);
const fps = ref(0);
const ***piledShaders = ref(0);
const renderTime = ref(0);
const gpuMemory = ref(0);
// 着色器代码编辑
const shaderCode = reactive({
vertex: `// 自定义顶点着色器代码
uniform float time;
uniform float intensity;
vec3 applyEffect(vec3 position) {
// 在这里添加自定义顶点变换
return position;
}`,
fragment: `// 自定义片段着色器代码
uniform float time;
uniform float intensity;
vec4 applyEffect(vec4 color) {
// 在这里添加自定义颜色处理
return color;
}`
});
// 渲染模式配置
const renderModes = [
{ id: 'standard', name: '标准模式' },
{ id: 'wireframe', name: '线框模式' },
{ id: 'normals', name: '法线可视化' },
{ id: 'depth', name: '深度图' },
{ id: 'custom', name: '完全自定义' }
];
// 计算属性
const currentRenderMode = ***puted(() => {
return renderModes.find(mode => mode.id === renderMode.value)?.name || '未知';
});
// Three.js 对象
let customRenderer, scene, camera, shaderManager;
let animationFrameId;
let frameCount = 0;
let lastFpsUpdate = 0;
// 初始化场景
const init = () => {
initRenderer();
initScene();
initShaderManager();
startAnimation();
};
// 初始化自定义渲染器
const initRenderer = () => {
customRenderer = new CustomWebGLRenderer({
canvas: renderCanvas.value,
antialias: true,
powerPreference: "high-performance"
});
customRenderer.setSize(window.innerWidth, window.innerHeight);
customRenderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
customRenderer.shadowMap.enabled = true;
customRenderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 添加性能监控回调
customRenderer.addRenderCallback(updatePerformanceStats);
};
// 初始化场景
const initScene = () => {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a1a);
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);
// 添加灯光
const ambientLight = new THREE.AmbientLight(0x404040, 0.4);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 10, 5);
directionalLight.castShadow = true;
scene.add(directionalLight);
// 创建测试几何体
createTestGeometry();
};
// 初始化着色器管理器
const initShaderManager = () => {
shaderManager = new ShaderManager();
};
// 创建测试几何体
const createTestGeometry = () => {
// 创建多个不同材质的几何体
const geometries = [
new THREE.BoxGeometry(1, 1, 1),
new THREE.SphereGeometry(0.7, 32, 32),
new THREE.ConeGeometry(0.7, 1.5, 32),
new THREE.TorusGeometry(1, 0.3, 16, 100)
];
const materials = [
new THREE.MeshStandardMaterial({ color: 0xff4444 }),
new THREE.MeshStandardMaterial({ color: 0x44ff44 }),
new THREE.MeshStandardMaterial({ color: 0x4444ff }),
new THREE.MeshStandardMaterial({ color: 0xffff44 })
];
geometries.forEach((geometry, index) => {
const material = materials[index % materials.length];
const mesh = new THREE.Mesh(geometry, material);
// 随机位置
mesh.position.set(
(Math.random() - 0.5) * 8,
(Math.random() - 0.5) * 8,
(Math.random() - 0.5) * 8
);
// 随机旋转
mesh.rotation.set(
Math.random() * Math.PI,
Math.random() * Math.PI,
Math.random() * Math.PI
);
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add(mesh);
});
// 添加地面
const groundGeometry = new THREE.PlaneGeometry(20, 20);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x666666,
roughness: 0.8,
metalness: 0.2
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -5;
ground.receiveShadow = true;
scene.add(ground);
};
// 设置渲染模式
const setRenderMode = (mode) => {
renderMode.value = mode;
switch (mode) {
case 'wireframe':
setWireframeMode();
break;
case 'normals':
setNormalsMode();
break;
case 'depth':
setDepthMode();
break;
case 'custom':
setCustomMode();
break;
default:
setStandardMode();
}
};
// 设置线框模式
const setWireframeMode = () => {
scene.traverse(object => {
if (object.isMesh) {
object.material.wireframe = true;
}
});
};
// 设置法线可视化模式
const setNormalsMode = () => {
// 这里应该实现法线可视化着色器
console.log('切换到法线可视化模式');
};
// 设置深度图模式
const setDepthMode = () => {
// 这里应该实现深度图渲染
console.log('切换到深度图模式');
};
// 设置自定义模式
const setCustomMode = () => {
customRenderer.setRenderOverride(customRenderFunction);
};
// 设置标准模式
const setStandardMode = () => {
scene.traverse(object => {
if (object.isMesh) {
object.material.wireframe = false;
}
});
customRenderer.setRenderOverride(null);
};
// 自定义渲染函数
const customRenderFunction = (scene, camera, renderer) => {
// 完全自定义的渲染流程
const gl = renderer.getContext();
// 清除
gl.clearColor(0.1, 0.1, 0.2, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 自定义渲染逻辑
renderCustomScene(scene, camera, renderer);
};
// 渲染自定义场景
const renderCustomScene = (scene, camera, renderer) => {
// 这里实现完全自定义的渲染逻辑
const renderList = [];
scene.traverse(object => {
if (object.isMesh && object.visible) {
renderList.push(object);
}
});
// 自定义排序
if (enableCustomSorting.value) {
renderList.sort(getSortFunction());
}
// 渲染每个对象
renderList.forEach(object => {
renderCustomObject(object, camera, renderer);
});
};
// 获取排序函数
const getSortFunction = () => {
switch (renderOrder.value) {
case 'frontToBack':
return (a, b) => {
const distA = a.position.distanceTo(camera.position);
const distB = b.position.distanceTo(camera.position);
return distA - distB;
};
case 'backToFront':
return (a, b) => {
const distA = a.position.distanceTo(camera.position);
const distB = b.position.distanceTo(camera.position);
return distB - distA;
};
case 'material':
return (a, b) => {
return a.material.id - b.material.id;
};
default:
return null;
}
};
// 渲染自定义对象
const renderCustomObject = (object, camera, renderer) => {
// 应用自定义着色器修改
applyShaderModifications(object.material);
// 使用原始渲染方法
renderer.renderObjectDirect(object, camera);
};
// 应用着色器修改
const applyShaderModifications = (material) => {
if (vertexModification.value !== 'none' || fragmentModification.value !== 'none') {
const customShader = shaderManager.createCustomShader(
`${vertexModification.value}_vertex`,
`${fragmentModification.value}_fragment`,
{
time: { value: timeUniform.value },
intensity: { value: intensityUniform.value }
}
);
material.userData.customShader = customShader;
material.needsUpdate = true;
}
};
// 更新性能统计
const updatePerformanceStats = () => {
// 更新帧率
frameCount++;
const now = performance.now();
if (now - lastFpsUpdate >= 1000) {
fps.value = Math.round((frameCount * 1000) / (now - lastFpsUpdate));
frameCount = 0;
lastFpsUpdate = now;
}
// 更新渲染时间
if (customRenderer.lastRenderTime) {
renderTime.value = customRenderer.lastRenderTime.toFixed(2);
}
// 更新其他统计信息(简化实现)
drawCalls.value = Math.floor(Math.random() * 50) + 10;
triangleCount.value = Math.floor(Math.random() * 10000) + 5000;
***piledShaders.value = Math.floor(Math.random() * 20) + 5;
gpuMemory.value = Math.floor(Math.random() * 500) + 100;
};
// 应用着色器代码
const applyShaderCode = () => {
// 这里应该将编辑的着色器代码应用到材质
console.log('应用着色器代码:', shaderCode);
};
// 重置着色器代码
const resetShaderCode = () => {
shaderCode.vertex = `// 自定义顶点着色器代码
uniform float time;
uniform float intensity;
vec3 applyEffect(vec3 position) {
// 在这里添加自定义顶点变换
return position;
}`;
shaderCode.fragment = `// 自定义片段着色器代码
uniform float time;
uniform float intensity;
vec4 applyEffect(vec4 color) {
// 在这里添加自定义颜色处理
return color;
}`;
};
// 动画循环
const startAnimation = () => {
const animate = () => {
animationFrameId = requestAnimationFrame(animate);
// 更新时间uniform
timeUniform.value += 0.016; // 约60FPS
// 旋转相机
if (camera) {
const time = Date.now() * 0.001;
camera.position.x = Math.cos(time) * 8;
camera.position.z = Math.sin(time) * 8;
camera.lookAt(0, 0, 0);
}
// 渲染场景
customRenderer.render(scene, camera);
};
animate();
};
onMounted(() => {
init();
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
if (customRenderer) {
customRenderer.dispose();
}
window.removeEventListener('resize', handleResize);
});
const handleResize = () => {
if (!camera || !customRenderer) return;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
customRenderer.setSize(window.innerWidth, window.innerHeight);
};
return {
// 模板引用
renderCanvas,
// 状态数据
renderMode,
vertexModification,
fragmentModification,
timeUniform,
intensityUniform,
enableCustomSorting,
enableMultiPass,
renderOrder,
showShaderEditor,
activeShaderTab,
drawCalls,
triangleCount,
fps,
***piledShaders,
renderTime,
gpuMemory,
shaderCode,
// 配置数据
renderModes,
// 计算属性
currentRenderMode,
// 方法
setRenderMode,
applyShaderCode,
resetShaderCode
};
}
};
</script>
<style scoped>
.custom-renderer-container {
width: 100%;
height: 100vh;
display: flex;
background: #000;
overflow: hidden;
}
.render-view {
flex: 1;
position: relative;
}
.render-canvas {
width: 100%;
height: 100%;
display: block;
}
.debug-info {
position: absolute;
top: 20px;
left: 20px;
background: rgba(0, 0, 0, 0.8);
padding: 15px;
border-radius: 8px;
color: white;
font-family: 'Courier New', monospace;
font-size: 14px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
min-width: 200px;
}
.info-item:last-child {
margin-bottom: 0;
}
.info-item span:first-child {
color: #***c;
}
.info-item span:last-child {
color: #00ff88;
font-weight: bold;
}
.control-panel {
width: 350px;
background: #2d2d2d;
padding: 20px;
overflow-y: auto;
border-left: 1px solid #444;
}
.panel-section {
margin-bottom: 25px;
padding-bottom: 20px;
border-bottom: 1px solid #444;
}
.panel-section:last-child {
margin-bottom: 0;
border-bottom: none;
}
.panel-section h3 {
color: #00ffff;
margin-bottom: 15px;
font-size: 16px;
}
.mode-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.mode-button {
padding: 12px 8px;
background: #444;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s ease;
}
.mode-button:hover {
background: #555;
}
.mode-button.active {
background: #00a8ff;
}
.shader-controls {
display: flex;
flex-direction: column;
gap: 15px;
}
.control-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.control-group label {
color: #***c;
font-size: 14px;
}
.control-group select {
padding: 8px 12px;
background: #444;
border: 1px solid #666;
border-radius: 4px;
color: white;
font-size: 14px;
}
.uniform-controls {
display: flex;
flex-direction: column;
gap: 8px;
}
.uniform-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.uniform-item span {
color: #***c;
font-size: 14px;
min-width: 60px;
}
.uniform-item input[type="range"] {
flex: 1;
}
.render-settings {
display: flex;
flex-direction: column;
gap: 15px;
}
.setting-group {
display: flex;
align-items: center;
gap: 10px;
}
.setting-group label {
color: #***c;
font-size: 14px;
cursor: pointer;
}
.setting-group input[type="checkbox"] {
margin: 0;
}
.setting-group select {
padding: 6px 10px;
background: #444;
border: 1px solid #666;
border-radius: 4px;
color: white;
font-size: 14px;
width: 100%;
}
.performance-stats {
display: flex;
flex-direction: column;
gap: 10px;
}
.stat-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
border-bottom: 1px solid #444;
}
.stat-item:last-child {
border-bottom: none;
}
.stat-item span:first-child {
color: #***c;
}
.stat-item span:last-child {
color: #00ff88;
font-weight: bold;
}
.shader-editor-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.editor-content {
background: #2d2d2d;
border-radius: 12px;
width: 90%;
max-width: 800px;
max-height: 90vh;
overflow: hidden;
border: 1px solid #444;
}
.editor-header {
padding: 20px;
background: #1a1a1a;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #444;
}
.editor-header h3 {
margin: 0;
color: #00ffff;
}
.close-button {
background: none;
border: none;
color: #***c;
font-size: 24px;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
.editor-body {
padding: 20px;
display: flex;
flex-direction: column;
gap: 15px;
}
.editor-tabs {
display: flex;
gap: 5px;
border-bottom: 1px solid #444;
}
.editor-tabs button {
padding: 10px 20px;
background: transparent;
color: #***c;
border: none;
border-bottom: 2px solid transparent;
cursor: pointer;
transition: all 0.3s ease;
}
.editor-tabs button.active {
color: #00ffff;
border-bottom-color: #00ffff;
}
.shader-textarea {
width: 100%;
height: 400px;
background: #1a1a1a;
color: #00ff88;
font-family: 'Courier New', monospace;
font-size: 14px;
border: 1px solid #444;
border-radius: 4px;
padding: 15px;
resize: vertical;
line-height: 1.4;
}
.editor-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
}
.action-button {
padding: 10px 20px;
background: #444;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
}
.action-button:hover {
background: #555;
}
.action-button.secondary {
background: #666;
}
/* 响应式设计 */
@media (max-width: 1024px) {
.custom-renderer-container {
flex-direction: column;
}
.control-panel {
width: 100%;
height: 400px;
}
.render-view {
height: calc(100vh - 400px);
}
}
@media (max-width: 768px) {
.mode-buttons {
grid-template-columns: 1fr;
}
.editor-content {
width: 95%;
}
.shader-textarea {
height: 300px;
}
}
</style>
高级特性
多通道渲染系统
// 多通道渲染管理器
class MultiPassRenderer {
constructor(renderer, scene, camera) {
this.renderer = renderer;
this.scene = scene;
this.camera = camera;
this.passes = [];
this.renderTargets = new Map();
this.setupRenderTargets();
}
setupRenderTargets() {
// 创建各种渲染目标
const parameters = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
stencilBuffer: false
};
this.renderTargets.set('color', new THREE.WebGLRenderTarget(
window.innerWidth, window.innerHeight, parameters
));
this.renderTargets.set('depth', new THREE.WebGLRenderTarget(
window.innerWidth, window.innerHeight, parameters
));
this.renderTargets.set('normals', new THREE.WebGLRenderTarget(
window.innerWidth, window.innerHeight, parameters
));
}
addPass(pass) {
this.passes.push(pass);
}
render() {
const currentRenderTarget = this.renderer.getRenderTarget();
// 执行每个渲染通道
this.passes.forEach((pass, index) => {
const inputRT = index > 0 ? this.passes[index - 1].outputRT : null;
const outputRT = index < this.passes.length - 1 ?
this.renderTargets.get(`pass_${index}`) : null;
pass.render(this.scene, this.camera, inputRT, outputRT);
});
this.renderer.setRenderTarget(currentRenderTarget);
}
}
// 基础渲染通道
class RenderPass {
constructor(name, renderFunction) {
this.name = name;
this.renderFunction = renderFunction;
this.outputRT = null;
}
render(scene, camera, inputRT, outputRT) {
this.renderFunction(scene, camera, inputRT, outputRT);
this.outputRT = outputRT;
}
}
动态着色器热重载
// 着色器热重载系统
class ShaderHotReload {
constructor() {
this.watchedShaders = new Map();
this.setupFileWatching();
}
setupFileWatching() {
// 监听着色器文件变化
if (typeof module !== 'undefined' && module.hot) {
module.hot.a***ept('./shaders/*.glsl', () => {
this.reloadShaders();
});
}
}
watchShader(material, shaderPath) {
this.watchedShaders.set(material.uuid, {
material,
shaderPath,
lastModified: Date.now()
});
}
async reloadShaders() {
for (const [uuid, shaderInfo] of this.watchedShaders) {
try {
const newShaderCode = await this.loadShader(shaderInfo.shaderPath);
this.updateMaterialShader(shaderInfo.material, newShaderCode);
} catch (error) {
console.error(`重载着色器失败: ${shaderInfo.shaderPath}`, error);
}
}
}
async loadShader(path) {
const response = await fetch(path);
return await response.text();
}
updateMaterialShader(material, shaderCode) {
if (material.isShaderMaterial) {
material.vertexShader = shaderCode;
material.needsUpdate = true;
}
}
}
本节展示了如何通过继承和重写Three.js渲染器来实现完全自定义的渲染管线。这种技术为高级图形效果、特殊渲染需求和性能优化提供了强大的基础。