You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust Nannou中为阴影投射的可见区域实现径向渐变

阴影投射算法的径向渐变实现问题

我已经实现了一套阴影投射算法,效果如下:
阴影投射效果

现在我想给可见区域(非阴影区域)添加径向渐变效果,模拟真实光源的衰减特性:靠近光标(光源位置)的区域更亮,远离的可见区域则更暗。

我能获取到的输入有两部分:

  • 可见区域:由Vec<Point2>表示,是按从光源到顶点的角度递增排序的有序顶点向量
  • 光源位置:由Point2表示

作为OpenGL和Rust Nannou的新手,我尝试过用Nannou的points_colored函数,但效果异常;也看过nannou示例里的draw/draw_mesh.rs,感觉相关但搞不懂三角形剖分的概念。想请教:

  • 怎么在Nannou里实现径向渐变?为什么需要创建三角形(或四边形)?

一、为什么需要三角形/四边形?

OpenGL(包括Nannou底层依赖的渲染管线)本质是基于三角形图元渲染复杂形状的。所有2D/3D图形最终都会被拆解成三角形网格——因为三角形是最稳定的平面图形,能保证渲染时的一致性和正确性。你的可见区域是一个多边形,要给它填充渐变颜色,必须先把这个多边形剖分成多个三角形(四边形最终也会拆成两个三角形),这样才能给每个顶点赋予颜色,让GPU自动插值出平滑的渐变效果。

二、Nannou中实现径向渐变的步骤

1. 把可见多边形剖分成三角形扇

你的顶点已经按从光源出发的角度递增排序,刚好可以直接构建三角形扇:以光源为中心顶点,依次连接相邻的两个可见区域顶点,形成一个个三角形。比如顶点序列是P0, P1, P2, ..., Pn,那么三角形就是[光源, P0, P1]、[光源, P1, P2]……直到[光源, Pn-1, Pn]。

2. 给每个顶点计算渐变颜色

径向渐变的核心是:顶点离光源越近,颜色越亮;越远越暗。你可以按以下逻辑计算每个顶点的颜色:

  • 定义基础亮色(比如白色rgb(1.0,1.0,1.0))和暗色(比如深灰rgb(0.2,0.2,0.2))
  • 计算每个可见顶点到光源的距离distance = point.distance(light_pos)
  • 设定一个最大衰减距离max_distance(比如屏幕对角线长度,根据你的窗口大小调整)
  • 计算衰减系数alpha = 1.0 - (distance / max_distance).clamp(0.0, 1.0)(确保系数在0-1之间)
  • 最终顶点颜色 = 亮色 * alpha + 暗色 * (1.0 - alpha)
  • 光源本身的顶点直接设为最亮的颜色

3. 用Nannou的Mesh绘制带颜色的三角形扇

直接用MeshBuilder构建带颜色的网格,示例代码如下:

use nannou::prelude::*;

fn draw_gradient_visible_area(draw: &Draw, light_pos: Point2, visible_points: &[Point2]) {
    // 可根据窗口大小调整最大衰减距离
    let max_distance = 500.0;
    let light_color = WHITE;
    let dark_color = rgb(0.2, 0.2, 0.2);

    let mut mesh = Mesh::new();
    // 添加光源顶点,颜色设为最亮
    mesh.add_vertex(Vertex {
        position: light_pos,
        color: light_color,
        tex_coords: Default::default(),
    });

    for (i, &point) in visible_points.iter().enumerate() {
        let next_point = visible_points[(i + 1) % visible_points.len()];
        
        // 计算当前顶点的颜色
        let dist = point.distance(light_pos);
        let alpha = 1.0 - (dist / max_distance).clamp(0.0, 1.0);
        let point_color = light_color * alpha + dark_color * (1.0 - alpha);
        
        // 计算下一个顶点的颜色
        let next_dist = next_point.distance(light_pos);
        let next_alpha = 1.0 - (next_dist / max_distance).clamp(0.0, 1.0);
        let next_point_color = light_color * next_alpha + dark_color * (1.0 - next_alpha);

        // 添加当前点和下一个点的顶点到网格
        let p1_idx = mesh.add_vertex(Vertex {
            position: point,
            color: point_color,
            tex_coords: Default::default(),
        });
        let p2_idx = mesh.add_vertex(Vertex {
            position: next_point,
            color: next_point_color,
            tex_coords: Default::default(),
        });
        
        // 添加三角形:光源 -> 当前点 -> 下一个点
        mesh.add_triangle(0, p1_idx, p2_idx);
    }

    draw.mesh(&mesh);
}

4. 替代方案:自定义着色器

如果觉得三角形剖分麻烦,也可以用自定义片段着色器:先绘制可见区域的蒙版,然后在片段着色器中根据每个像素到光源的距离直接计算颜色。不过对于新手来说,三角形扇的方法更直观,不需要编写GLSL代码。


内容的提问来源于stack exchange,提问作者Enigmatisms

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 11:16:03