GLSL光线步进轨道相机拖动状态变量重置问题求助
轨道相机GLSL着色器鼠标拖拽问题排查与修复
问题分析
你在Shadertoy中实现光线步进轨道相机时遇到以下问题:
- 左键点击保存初始鼠标坐标时,
startDraggingPos始终记录当前鼠标位置 - 调试发现
mainImage的第一个if分支持续触发,导致点击时屏幕变黑 isDragging变量被反复重置
核心原因是Shadertoy中的全局变量没有帧间持久性——每帧渲染时,isDragging、startDraggingPos这类全局变量都会被重新初始化,导致你无法用它们保存跨帧的拖拽状态。
原代码问题点
// Camera control vec2 cameraAng = vec2(0.5, 0.5); bool isDragging = false; // 每帧都会重置为false vec2 startDraggingPos = vec2(-1); // 每帧都会重置为(-1,-1) void mainImage(out vec4 c, vec2 p) { vec2 uv = (gl_FragCoord.xy - 0.5*iResolution.xy) / iResolution.y; vec2 mouseUV = vec2(0.5); mouseUV = iMouse.xy/iResolution.xy; // [0,1] // 因为isDragging每帧都是false,所以这个分支每帧都会触发 if(iMouse.z > 0.0 && (iMouse.x > 0.0 || iMouse.y > 0.0) && !isDragging){ isDragging = true; startDraggingPos = mouseUV; c = vec4(0.0,0.0, 0.0,1.0); return; } else if(iMouse.z < 0.0 || (iMouse.x <= 0.0 || iMouse.y <= 0.0)){ isDragging = false; } if(isDragging){ vec2 inc = (mouseUV - startDraggingPos) * 10.0; cameraAng += inc; } // ··· }
修复方案
利用Shadertoy提供的iMouse.w变量(鼠标按下第一帧为1.0,后续保持0.0直到释放)判断拖拽起始,同时通过Buffer通道保存跨帧状态(如相机角度)。
1. 基础修复(无需Buffer,仅实现拖拽时角度变化)
// Camera control vec2 cameraAng = vec2(0.5, 0.5); vec2 startDraggingPos = vec2(-1); void mainImage(out vec4 c, vec2 p) { vec2 uv = (gl_FragCoord.xy - 0.5*iResolution.xy) / iResolution.y; vec2 mouseUV = iMouse.xy / iResolution.xy; // 仅在鼠标按下的第一帧记录初始位置 if(iMouse.z > 0.0 && iMouse.w > 0.0){ startDraggingPos = mouseUV; } // 鼠标释放时重置初始位置标记 else if(iMouse.z == 0.0){ startDraggingPos = vec2(-1); } // 拖拽状态下更新相机角度 if(iMouse.z > 0.0 && startDraggingPos.x != -1){ vec2 inc = (mouseUV - startDraggingPos) * 10.0; // 这里如果需要保留角度,需用Buffer存储,否则每帧会重置为(0.5,0.5) cameraAng = vec2(0.5, 0.5) + inc; } // 光线步进渲染逻辑... // 移除原调试的return语句,避免屏幕变黑 // ··· }
2. 持久化相机角度(使用Buffer通道)
如果需要让相机角度在拖拽停止后保持不变,必须用Buffer存储状态:
Buffer A 着色器(负责保存相机角度和初始拖拽位置)
void mainImage(out vec4 c, vec2 p) { // 读取上一帧的相机角度和初始拖拽位置 vec2 cameraAng = texture(iChannel0, vec2(0.5)).xy; vec2 startDragPos = texture(iChannel0, vec2(0.5)).zw; vec2 mouseUV = iMouse.xy / iResolution.xy; // 鼠标按下第一帧,记录初始位置 if(iMouse.z > 0.0 && iMouse.w > 0.0){ startDragPos = mouseUV; } // 拖拽中更新相机角度 else if(iMouse.z > 0.0){ vec2 inc = (mouseUV - startDragPos) * 10.0; cameraAng += inc; } // 保存状态到Buffer c = vec4(cameraAng, startDragPos); }
主着色器(读取Buffer中的相机角度)
void mainImage(out vec4 c, vec2 p) { vec2 uv = (gl_FragCoord.xy - 0.5*iResolution.xy) / iResolution.y; // 从Buffer读取持久化的相机角度 vec2 cameraAng = texture(iChannel0, vec2(0.5)).xy; vec2 mouseUV = iMouse.xy / iResolution.xy; // 光线步进渲染逻辑,使用cameraAng计算相机射线 // ··· }
关键知识点
- Shadertoy的全局变量是每帧重新初始化的,无法保存跨帧状态
iMouse.w是判断鼠标按下起始帧的关键变量,避免重复记录初始位置- 跨帧状态(如相机角度)必须通过Buffer通道存储,才能在帧间保持
内容的提问来源于stack exchange,提问作者Daniel Zufrí Quesada
相关产品推荐
相关产品推荐

