当前位置: 首页 > news >正文

Metal入门,使用Metal实现灯光效果和噪点效果

灯光效果

首先了解一下X,Y,Z在3D着色器中的概念:
x轴:水平方向(从左到右)
y轴:垂直方向(从下到上)
z轴:垂直于屏幕的方向(从屏幕指向观察者)

然后我们是使用的内核进行渲染

kernel void compute(texture2d<float, access::write> output [[texture(0)]],constant float &timer [[buffer(1)]],constant float2 &clickPos [[buffer(2)]],uint2 gid [[thread_position_in_grid]])
{// gid 可以理解为像素坐标int width = output.get_width();int height = output.get_height();float2 uv = float2(gid) / float2(width, height);// 调整UV坐标以考虑屏幕宽高比float aspect = float(width) / float(height);float2 centered = uv - 0.5;// 如果宽度大于高度,调整x坐标if (aspect > 1.0) {centered.x *= aspect;} // 如果高度大于宽度,调整y坐标else {centered.y /= aspect;}float radius = 0.4; // 稍微缩小半径确保在所有设备上都可见float distance = length(centered);float z = sqrt(radius * radius - centered.x * centered.x - centered.y * centered.y);//法线向量float3 normal = normalize(float3(centered.x, centered.y, z));//光源float3 source = normalize(float3(cos(timer), sin(timer), 0.5));float light = dot(normal,source);output.write(distance <= radius ? float4(float3(light),1) : float4(0),gid);
}
  1. 一开始的操作都比较基础,获取长宽,然后计算uv坐标
  2. 通过屏幕宽高比,对uv坐标进行调整
  3. 计算每个点的z坐标(高度)值,其用于构建法线向量
  4. 法线向量决定了表面如何反射光线
  5. 光线反射(通过点积计算)决定了表面的明暗

光源方向向量的几何理解

在3D图形中,我们使用方向向量来表示光源的照射方向。在代码中:

float3 source = normalize(float3(cos(timer), sin(timer), 0.5));

这个向量有三个分量:

  • x = cos(timer):水平方向
  • y = sin(timer):垂直方向
  • z = 0.5:深度方向(从屏幕向内)

normalize函数确保这个向量的长度为1,但保持其方向不变。

为什么z值影响照射角度?

想象一个单位球体在坐标原点,光源方向向量从球心指向外部:

  1. z值与方向角度的关系

    • 当z=0时,向量完全在xy平面内(水平面),与z轴垂直(90°角)
    • 当z增大时,向量逐渐倾向于z轴方向,与z轴的夹角减小
    • 当z=1且x=y=0时,向量完全沿z轴方向(0°角)
  2. 具体数值的影响

    • z=0.5时:向量与z轴的夹角约为60°,更多地倾向于水平方向
    • z=1时:向量与z轴的夹角约为45°,在水平和垂直方向上均衡
    • z=2时:向量与z轴的夹角约为27°,更多地倾向于垂直方向

通过归一化的向量值计算

当x=cos(0)=1, y=sin(0)=0时的三种情况:

  1. z=0.5:

    • 未归一化的向量: (1, 0, 0.5)
    • 向量长度: √(1² + 0² + 0.5²) = √1.25 ≈ 1.118
    • 归一化后: (1/1.118, 0, 0.5/1.118) ≈ (0.894, 0, 0.447)
    • 这个向量与z轴的夹角: arccos(0.447) ≈ 63.4°
  2. z=1:

    • 未归一化的向量: (1, 0, 1)
    • 向量长度: √(1² + 0² + 1²) = √2 ≈ 1.414
    • 归一化后: (1/1.414, 0, 1/1.414) ≈ (0.707, 0, 0.707)
    • 这个向量与z轴的夹角: arccos(0.707) ≈ 45°
  3. z=2:

    • 未归一化的向量: (1, 0, 2)
    • 向量长度: √(1² + 0² + 2²) = √5 ≈ 2.236
    • 归一化后: (1/2.236, 0, 2/2.236) ≈ (0.447, 0, 0.894)
    • 这个向量与z轴的夹角: arccos(0.894) ≈ 26.6°

对光照效果的影响

在着色器中,光照强度通过点积计算:

float light = dot(normal, source);
  1. 侧面照射(z较小)

    • 当z=0.5时,光源更多地从侧面照射
    • 球体边缘的法线主要在xy平面内,与这种光源方向更容易形成较大的点积
    • 结果:球体边缘在光源方向上的部分会很亮,对面的部分很暗,形成强烈的侧面光照效果
  2. 正面照射(z较大)

    • 当z=2时,光源更多地从正面(z轴方向)照射
    • 球体中心区域的法线主要沿z轴方向,与这种光源方向形成较大的点积
    • 结果:球体中心区域较亮,边缘普遍较暗,形成聚光灯效果

简言之,z值决定了光源"高度"的感觉 - z值小时光源像是在球体侧面旋转,z值大时光源像是从球体上方照射。这直接影响了球体表面的明暗分布,塑造了不同的3D视觉效果。
在这里插入图片描述

噪点效果

float random(float2 p)
{return fract(sin(dot(p, float2(15.79, 81.93)) * 45678.9123));
}float noise(float2 p)
{float2 i = floor(p);float2 f = fract(p);f = f * f * (3.0 - 2.0 * f);//可以消除随机噪声的锯齿状边缘,产生自然过渡的纹理效果,生成连续平滑噪声float bottom = mix(random(i + float2(0)), random(i + float2(1.0, 0.0)), f.x);float top = mix(random(i + float2(0.0, 1.0)), random(i + float2(1)), f.x);float t = mix(bottom, top, f.y);return t;
}float fbm(float2 uv)
{float sum = 0;float amp = 0.7;for(int i = 0; i < 4; ++i){sum += noise(uv) * amp;uv += uv * 1.2;amp *= 0.4;}return sum;
}kernel void compute(texture2d<float, access::write> output [[texture(0)]],constant float &timer [[buffer(1)]],constant float2 &clickPos [[buffer(2)]],uint2 gid [[thread_position_in_grid]])
{int width = output.get_width();int height = output.get_height();float2 uv = float2(gid) / float2(width, height);// 调整UV坐标以考虑屏幕宽高比float aspect = float(width) / float(height);float2 centered = uv - 0.5;// 如果宽度大于高度,调整x坐标if (aspect > 1.0) {centered.x *= aspect;} // 如果高度大于宽度,调整y坐标else {centered.y /= aspect;}float radius = 0.4; // 稍微缩小半径确保在所有设备上都可见float distance = length(centered);//    通过将坐标沿x轴随时间移动(timer*0.2)创建动画效果
//    使用fmod函数确保坐标值不会超出纹理范围
//    相当于让噪声图案向右缓慢移动,速度由0.2系数控制
//    只在x方向移动(第二个分量为0)centered = fmod(centered + float2(timer*0.2,0), float2(width,height));//    对处理后的坐标应用fbm(分形布朗运动)函数生成噪声值
//    乘以3是缩放因子,让噪声纹理更细致(频率更高)float t = fbm(centered * 3);output.write(distance <= radius ? float4(float3(t),1) : float4(0),gid);
}

在这里插入图片描述

相关文章:

  • Linux LVM管理
  • 怎么把https://github.com项目拉到自己的github
  • 高性能算法RIME:基于物理的优化的霜冰优化算法
  • 云原生+大数据
  • python调用底层c++算子示例
  • Docker常用命令介绍
  • [欠拟合过拟合]机器学习-part10
  • Phantom 视频生成的流程
  • HarmonyOS学习——UIAbility组件(下)
  • 鸿蒙App开发学习路径
  • (第95天)OGG 微服务搭建 Oracle 19C 到 MySQL 8 双向同步
  • 【Jaspersoft studio 生成的模板,无法分页的问题】
  • 卓力达靶标:精密制造赋能材料沉积技术革新
  • docker-volume-backup 备份 ragflow volumes
  • 五元组+协议分层:拆解网络通信的底层密码
  • Metal入门,使用Metal实现纹理效果
  • [C++面试] 基础题
  • const修饰指针
  • 【网络篇】TCP协议的三次握手和四次挥手
  • 如何让Wi-Fi设备传输距离达到1100米?涂鸦新方案让通信距离远超传统5倍
  • 专门提供做ppt小素材的网站/市场调研的方法
  • 佛山做网站建设公司/uc推广登录入口
  • 个人做营利性质网站会怎么样/免费制作网页的网站
  • 怎样制作时时彩网站做/如何策划一个营销方案
  • jmail官方网站/房地产十大营销手段
  • 淘宝优惠劵网站怎么做/百度小说排行