自由学习记录(146)
https://www.cnblogs.com/timlly/p/10631718.htmlmatlab从来不知道这样的东西...和python的关系--大致上和py也挺像的但py还是有一些反复写的.....这样的库关系让代码不受待见python的确可以做一样的事情但现在gpt里也可以直接run python了环境也不用自己配了。同时有些可执行脚本会放到C:\Python313\Scripts你可以把它理解成Lib\site-packages Python 第三方库仓库Scripts 这个环境附带的一些命令工具pip install ...安装的是“当前 Python 环境的依赖”不是“全电脑通用插件”。你用哪个python.exe运行脚本就用哪个python.exe -m pip install ...装库matplotlib 很难稳定跑到 60 FPS尤其在 Windows 和交互窗口里。vscodeF5运行并调试CtrlF5不调试直接运行https://zhuanlan.zhihu.com/p/141595120matlab转python也很快但里面运行起来的确如博主说的很卡import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation def rotation_matrix(theta: float) - np.ndarray: c np.cos(theta) s np.sin(theta) return np.array([[c, -s], [s, c]], dtypenp.float64) def rotate_about(points: np.ndarray, theta: float, center: np.ndarray) - np.ndarray: return rotation_matrix(theta) (points - center[:, None]) center[:, None] def build_geneva_geometry(a: float, r_nominal: float, r_pin: float, r_s: float, h: float): center_geneva np.array([a, 0.0], dtypenp.float64) xy_geneva_1 np.zeros((2, 183), dtypenp.float64) xy_geneva_1[:, 0] [a - r_nominal, -r_pin] index 1 for angle_deg in np.linspace(-90.0, 90.0, 181): theta np.deg2rad(angle_deg) xy_geneva_1[:, index] np.array( [r_pin * np.cos(theta), r_pin * np.sin(theta)], dtypenp.float64, ) np.array([a - r_nominal h - r_pin, 0.0], dtypenp.float64) index 1 xy_geneva_1[:, index] [a - r_nominal, r_pin] xy_geneva_2 rotate_about(xy_geneva_1, np.pi / 2, center_geneva) xy_geneva_3 rotate_about(xy_geneva_2, np.pi / 2, center_geneva) xy_geneva_4 rotate_about(xy_geneva_3, np.pi / 2, center_geneva) xy_geneva_circle_1 np.zeros((2, 0), dtypenp.float64) xy_geneva_circle_1 np.column_stack((xy_geneva_circle_1, xy_geneva_1[:, -1])) for angle_deg in np.linspace(-180.0, 180.0, 361): alpha np.deg2rad(angle_deg) x r_s * np.cos(alpha) - a y r_s * np.sin(alpha) rotated rotation_matrix(-np.pi / 4) np.array([x, y], dtypenp.float64) rotated center_geneva if np.max(np.abs([rotated[0] - a, rotated[1]])) r_nominal: xy_geneva_circle_1 np.column_stack((xy_geneva_circle_1, rotated)) xy_geneva_circle_1 np.column_stack((xy_geneva_circle_1, xy_geneva_4[:, 0])) xy_geneva_circle_2 rotate_about(xy_geneva_circle_1, np.pi / 2, center_geneva) xy_geneva_circle_3 rotate_about(xy_geneva_circle_2, np.pi / 2, center_geneva) xy_geneva_circle_4 rotate_about(xy_geneva_circle_3, np.pi / 2, center_geneva) slots [xy_geneva_1, xy_geneva_2, xy_geneva_3, xy_geneva_4] arcs [xy_geneva_circle_1, xy_geneva_circle_2, xy_geneva_circle_3, xy_geneva_circle_4] return center_geneva, slots, arcs def main(): a 150.0 r 150.0 * np.cos(np.pi / 4) r_nominal r r_pin 10.0 r_s 75.0 h 80.0 total_angle 360 * 4 n total_angle * 2 1 delta_theta_deg 0.5 omega 15 * 2 * np.pi / 60 delta_time np.deg2rad(delta_theta_deg) / omega circle_theta np.deg2rad(np.linspace(0.0, 360.0, 361)) circle_pin np.vstack((r_pin * np.cos(circle_theta) r, r_pin * np.sin(circle_theta))) motion_theta np.deg2rad(np.linspace(0.0, total_angle, n)) xy_pin np.vstack((r * np.cos(motion_theta), r * np.sin(motion_theta))) xy_driver_plate np.zeros((2, 273), dtypenp.float64) arc_theta np.deg2rad(np.linspace(45.0, 315.0, 271)) xy_driver_plate[:, 1:272] np.vstack((r_s * np.cos(arc_theta), r_s * np.sin(arc_theta))) center_geneva, slots, arcs build_geneva_geometry(a, r_nominal, r_pin, r_s, h) basis_center center_geneva[:, None] theta_geneva np.zeros(n, dtypenp.float64) delta_theta_geneva 90.0 stop_theta_geneva 0.0 frame_data [] for index, theta in enumerate(motion_theta): driver_rot rotation_matrix(theta) circle_pin_temp driver_rot circle_pin driver_plate_temp driver_rot xy_driver_plate geneva_center_pin_vec xy_pin[:, index] - center_geneva if np.linalg.norm(geneva_center_pin_vec) r_nominal: cos_theta_geneva geneva_center_pin_vec[0] / np.linalg.norm(geneva_center_pin_vec) theta_geneva[index] np.rad2deg(np.arccos(np.abs(cos_theta_geneva))) if xy_pin[1, index] 0: theta_geneva[index] -theta_geneva[index] delta_theta_geneva / 2 stop_theta_geneva else: theta_geneva[index] theta_geneva[index] delta_theta_geneva / 2 stop_theta_geneva else: theta_geneva[index] theta_geneva[index - 1] if index 0 else 0.0 stop_theta_geneva theta_geneva[index] geneva_theta -np.deg2rad(theta_geneva[index] - delta_theta_geneva / 2) geneva_rot rotation_matrix(geneva_theta) slots_temp [geneva_rot (slot - basis_center) basis_center for slot in slots] arcs_temp [geneva_rot (arc - basis_center) basis_center for arc in arcs] frame_data.append( { driver_plate: driver_plate_temp, pin_line: np.array([[0.0, xy_pin[0, index]], [0.0, xy_pin[1, index]]], dtypenp.float64), pin_circle: circle_pin_temp, slots: slots_temp, arcs: arcs_temp, } ) omega_geneva np.zeros(n, dtypenp.float64) omega_geneva[0] (theta_geneva[1] - theta_geneva[0]) / delta_time omega_geneva[1:-1] (theta_geneva[2:] - theta_geneva[:-2]) / (2 * delta_time) omega_geneva[-1] (theta_geneva[-1] - theta_geneva[-2]) / delta_time fig, (ax_anim, ax_angle, ax_omega) plt.subplots(3, 1, figsize(8, 12)) fig.canvas.manager.set_window_title(09 Geneva Mechanism) ax_anim.set_xlim(-150, 300) ax_anim.set_ylim(-150, 150) ax_anim.set_aspect(equal, adjustablebox) ax_anim.grid(False) ax_anim.set_title(Geneva Mechanism Animation) ax_anim.plot([-120, 270], [0, 0], -.k, linewidth1) ax_anim.plot([0, 0], [-120, 120], -.k, linewidth1) ax_anim.plot(center_geneva[0], center_geneva[1], o, markersize5, markerfacecolorblack, markeredgecolorblack) driver_plate_line, ax_anim.plot([], [], k, linewidth2) pin_line, ax_anim.plot([], [], k, linewidth2) pin_circle_line, ax_anim.plot([], [], k, linewidth2) slot_lines [ax_anim.plot([], [], k, linewidth2)[0] for _ in range(4)] arc_lines [ax_anim.plot([], [], k, linewidth2)[0] for _ in range(4)] ax_angle.plot(np.linspace(1, total_angle, n), theta_geneva - delta_theta_geneva / 2) ax_angle.grid(True) ax_angle.set_title(Geneva Angle vs Driver Angle) ax_angle.set_xlabel(Driver angle (deg)) ax_angle.set_ylabel(Geneva angle (deg)) ax_omega.plot((np.linspace(1, total_angle, n) * np.pi / 180) / omega, omega_geneva) ax_omega.grid(True) ax_omega.set_title(Geneva Angular Velocity vs Time) ax_omega.set_xlabel(Time (s)) ax_omega.set_ylabel(Angular velocity (rad/s)) def update(frame_index: int): current frame_data[frame_index] driver_plate_line.set_data(current[driver_plate][0], current[driver_plate][1]) pin_line.set_data(current[pin_line][0], current[pin_line][1]) pin_circle_line.set_data(current[pin_circle][0], current[pin_circle][1]) for artist, points in zip(slot_lines, current[slots]): artist.set_data(points[0], points[1]) for artist, points in zip(arc_lines, current[arcs]): artist.set_data(points[0], points[1]) return [driver_plate_line, pin_line, pin_circle_line, *slot_lines, *arc_lines] animation FuncAnimation( fig, update, frameslen(frame_data), interval50, blitFalse, repeatTrue, cache_frame_dataFalse, ) plt.tight_layout() fig._animation animation plt.show() if __name__ __main__: main()https://www.disneyanimation.com/open-source/?https://www.cnblogs.com/timlly/p/10631718.html这个引用gpu gem 看到过很多次了这个siggraph Course也不知道是什么的确每个slide和Course note里都有很多东西能把这些引用都带上。https://renderwonk.com/publications/s2010-shading-course/课程评审材料还特别强调要让“来自不同专业背景的 jury”能看懂课程结构和收获这本身就说明它面向的是跨背景、跨层次受众而不是单纯 beginner 教学。Courses 往往是专题拼装不是单讲师长课。一个 topic 往往会拆成多个子块由不同讲者分别负责。这样做的优点是每一段都由更贴近该子方向的一线人来讲信息密度高缺点就是统一叙事、统一术语、统一前置知识铺垫往往没那么强所以看起来会像“几个人轮流做 mini-talk”。这不是你错觉而是 conference tutorial 常见形态。SIGGRAPH 本身是跨行业社区不是单一学科课堂。官方一直把它定位成覆盖 research、development、creativity、production 等多个面向的 interdisciplinary educational experience。这样的会议环境下course 设计天然更像“把不同视角的人拉到一个专题里”而不是按教科书逻辑从基础定义一路讲到习题。https://www.cnblogs.com/timlly/p/11098212.html皮肤高光的主要贡献者。油脂oil。表皮epidermis。油脂层下是表皮覆盖是造成次表面散射的物质之一。“屏幕上的数值并不等于你眼睛感受到的亮度。”也就是RGB 数值和视觉亮度不是线性对应的。你可以把图拆开理解。左右两边的竖条区域其实不是纯灰。它们是由很多黑白细线组成的黑 0白 255因为黑白线一半一半远一点看时你眼睛会把它平均掉感觉成一个“中间灰”平均值大约是 127.5。中间有两个纯色矩形上面的矩形数值是 128下面的矩形数值是 187作者让你退远看是为了让你把左右两边的黑白线“看成一整块灰”。这时候你会发现左右两边那种“黑白平均后的灰”看起来更接近下面那个 187 的灰块而不是更接近上面那个 128 的灰块。这就是作者想表达的关键虽然左右两边平均数值是 127.5和 128 非常接近但视觉上它却不像 128反而更像 187。因此在显示器上所显示出来的效果就是Gamma空间换句话说大多数的美术人员都是工作在Gamma空间下的。这个校正在大多数显示器或者游戏引擎中都是以pow(x, 2.2)的形式存在。根据不同的显示器Gamma曲线可能也有不同例如Mac的显示器的Gamma曲线为1.8.在游戏中常用的sRGB也与Gamma曲线相接近。很多时候可以通过Gamma校正来对图像进行一些操作。例如当图像太亮则可以使用使用pow(x, 2.2)的修正来让它变得正常一些。显示器将图片调亮了那么存储在硬盘中的图像比起显示器中的图像要暗一些。这样就解释了没有经验的打印店老板所打印的美术作品有时会偏暗的原因。正因为这个原因大多数的商业照相机都内置了Gamma校正 —— 通过将图像存储在Gamma空间中当传感器接收到光照的时候摄像机会使用pow(x, 1/2.2)再进行量化。因此储存的图像要比真实场景更亮一些只是在显示在显示器的时候这个差值会被修正。在亮处的地方颜色的衰减比较快在暗处的地方颜色的衰减比较慢整体上看起来非常柔和。另一方面色彩的偏移非常明显尤其在Specular高光那里尤其明显。、https://blog.csdn.net/noahzuo/article/details/51542607如果这张图片是“颜色贴图 / BaseColor / Albedo / UI 颜色图”这类用于显示颜色的纹理磁盘里通常就是按sRGB / gamma 编码存的。这时候导入引擎时开启 sRGB意思是“这张纹理文件里的数值不是线性光能值而是 gamma 编码后的显示值。采样时请先帮我转回 linear再参与运算。”磁盘里的图片sRGB / gamma 编码导入纹理勾选 sRGBShader 采样后自动得到 linear 值在线性空间做光照、混合、插值最后输出到屏幕时再做一次 linear - sRGB/gamma所以你说的“开着 sRGB 就可以”如果前提是这张图本来就是给人看的颜色图那基本就是对的。“linear - sRGB/gamma” 这一步通常是 GPU/渲染管线在输出阶段做的不是显示器替你把线性值自动变成 gamma。现实里的显示链路整体上接近一个非线性响应所以大家约定用sRGB/gamma 编码来存储和传输“最终给显示看的图像”。最后写回 LDR 显示目标时GPU/图形 API 会把 linear 再编码成 sRGB。显示器收到的是这种“适合显示链路”的非线性编码值然后显示出来。GPU 往显示链路送出的通常已经是编码后的 sRGB/gamma 风格值显示器不是替你做一次标准的 linear-gamma 编码而是按自己的电光响应把输入码值变成实际亮度。可以把显示器理解成“物理发光设备”不是“颜色空间转换器”。如果是 HDR / 浮点 RT中间过程往往保持 linear不会立刻 sRGB encode。等到最后 tone mapping display mapping 那一步才转成最终显示格式。最后一次对dgf/4nov·nol发起冲锋你可以这样分层D有多少微表面朝着合适方向F这些微表面在该角度会反多少G这些微表面会不会被遮挡/阴影掉这三项都在回答“微表面自己会发生什么”分母在回答“这些发生在微表面的事换算到宏观表面、单位入射辐照度、单位出射方向下应当怎么算才物理一致”所以你的这句话这个分母是这些规则统一的共同影响属性我会改成更准确的版本它不是 D/F/G 的共同“效果项”而是把 D/F/G 放进 BRDF 定义后必须满足的共同“归一化/几何换算项”。也就是说D/F/G 决定高光“长什么样、强多少、会不会被挡”分母决定“这种结果以 BRDF 的形式表达时量纲、角度关系、能量关系是不是对的”Jacobian 可以先把它理解成“变量换了以后面积/体积/概率密度要怎么跟着改的换算系数。”它不是图形学专属词是数学里做“变量替换”时的标准工具。最直白的例子你有一条线段用变量 xxx 表示。现在改用另一个变量 uuux2ux 2ux2u那 uuu 增加一点点 dududu对应的 xxx 不是增加同样多而是dx2 dudx 2\,dudx2du这里这个2就是最简单的一维 Jacobian。意思就是“你虽然只是换了个变量名但这个变量空间里的‘一点点长度’在另一个空间里其实不是一样大所以积分、密度、概率都要乘这个比例。”放到二维更容易看出它为什么重要。假设你有平面坐标 (x,y)(x,y)(x,y)换成极坐标 (r,θ)(r,\theta)(r,θ)xrcosθ,yrsinθx r\cos\theta,\quad y r\sin\thetaxrcosθ,yrsinθ这时候面积元不是dx dydr dθdx\,dy dr\,d\thetadxdydrdθ而是dx dyr dr dθdx\,dy r\,dr\,d\thetadxdyrdrdθ这里多出来的那个 rrr本质上就是 Jacobian。为什么会多一个 rrr因为在极坐标里θ\thetaθ 变化同样一点点半径越大扫出来的弧长越大面积当然也越大。所以变量换了面积密度也必须跟着换。所以 Jacobian 的核心用途就是当你把“某个量的分布”从一种参数表示换到另一种参数表示时保证积分结果不变。比如概率密度换变量面积元、体积元换坐标方向分布换参数图形学里从 hhh 空间换到 l,vl,vl,v 空间都要用 Jacobian。为什么会冒出那个 4因为你最开始描述的是“微表面法线朝向 hhh 的分布”也就是 D(h)D(h)D(h)但最后 BRDF 要写成“给定入射方向 lll 和观察方向 vvv反射是多少”这里就发生了变量替换从“用微表面朝向 hhh 描述”换成“用方向 l,vl,vl,v 描述”一旦换变量密度就不能直接照搬必须乘上对应的 Jacobian。那个推导最后整理出来就会落到分母里的那一整套包括你一直追的那个 4。所以你现在可以把 Jacobian 暂时记成一句很工程的话“我换了描述变量后为了让同一个物理量不变必须补上的缩放系数。”再给你一个特别适合图形学的直觉你原来在一个坐标系里看1 平方厘米就是 1 平方厘米。但换个扭曲过的坐标系后屏幕上看着同样一个小格子对应真实空间可能不是同样大。Jacobian 就是在告诉你“这个小格子到底被拉伸了多少、压缩了多少。”D(h)本来是“微表面法线分布”它的定义域是在half-vector / 微表面法线方向 hhh这个空间里但最终 BRDF 要写成 l,v,nl, v, n 的函数所以当你把“按 hh 分布的密度”转成“按 l,vl,v表达的 BRDF”时要做变量变换这个变换里冒出来的 Jacobian最后会和分母一起形成那一项所以你会感觉4 很像是从 D 那边带出来的这个直觉基本是对的。BRDF想知道的D里要表示的东西不是同一个D不是在描述“方向v上有多少能量”D 描述的是“法线朝向 hh 的微面有多少”。但 BRDF 想知道的是“已知l往v方向反多少”所以中间要翻译“法线空间里的一个小区域 dωhd\omega_h”对应到“观察方向空间里的一个小区域 dωvd\omega_v”到底差多少这个“差多少”就是 Jacobian 在管的事。现在最关键的几何关系是hlv∣lv∣h \frac{lv}{|lv|}也就是说hh 是 ll 和 vv的中间方向。但注意这不是普通的“平均一下”。因为 hh 还受镜面反射约束只有法线正好朝 hh 的微面才会把 ll 反到 vv。所以一个很小的 vv 变化不会对应同样大小的 hh 变化。vv 空间和 hh 空间的“格子大小”不一样。所以你把“按 hhh 计的分布密度”换成“按 vvv 计的反射密度”时必须补这个比例。分母只和D有关系那为什么不塞到D里D 本身有独立定义和归一化条件。D 是normal distribution function本质上是在说“单位宏观表面面积上微表面法线朝各个方向是怎么分布的。”它不是“最终高光强度函数”而是一个法线方向分布密度。所以它自己要满足自己的归一化规则比如那种∫ΩD(h) (n⋅h) dωh1\int_{\Omega} D(h)\,(n\cdot h)\,d\omega_h 1∫ΩD(h)(n⋅h)dωh1这个条件的意思是所有微表面的投影面积加起来要对应回宏观表面的面积。所以 D 是一个很“干净”的对象它只负责“微面法线统计”。如果你把分母里那些和 BRDF 定义有关的换算全塞进 DD 就不再是纯粹的 NDF 了。它会从“法线分布函数”变成“某种掺了方向换元和投影项的混合物”。这样概念就脏了。如果你把分母塞回 D就会出现问题你写出来一个所谓的 “D”D′D4(n⋅l)(n⋅v)D \frac{D}{4(n\cdot l)(n\cdot v)}那这个 D 就已经不是 normal distribution function 了。因为它现在依赖的不只是hh还依赖 ll 和 vv。原本的 D 是D(h)D(h)D(h)只描述法线朝向分布。但你塞完以后D 变成D′(h,l,v)D(h,l,v)那它就不再是“distribution of normals”了。它其实已经偷偷把 BRDF 的方向依赖也吞进去了。这在数学上当然能“写”但在概念上就不好了。你当然可以把整个式子重新命名成一个大函数S(l,v,h)D(h)F(l,h)G(l,v,h)4(n⋅l)(n⋅v)S(l,v,h)\frac{D(h)F(l,h)G(l,v,h)}{4(n\cdot l)(n\cdot v)}S(l,v,h)4(n⋅l)(n⋅v)D(h)F(l,h)G(l,v,h)然后写成frSf_r SfrS数学上完全没问题。但这样你失去了拆分模型的意义。你再也看不出哪部分是分布哪部分是 Fresnel哪部分是 masking哪部分是测度归一化所以不是不能“塞”而是一旦塞进去D 就不再叫 D 了。这才是关键。你这问题其实和物理里很多“为什么不并项”是一样的。可以并但并完以后会失去结构信息。再给你一个很贴切的类比。假设你有“速度 路程 / 时间”你问既然时间总在分母为什么不把它塞进“路程”里定义一个新路程当然可以。你可以定义“有效路程 路程 / 时间”那公式就变成“速度 有效路程”数学上没毛病。但“有效路程”已经不是原来的路程了它把时间因素偷吃进去了物理意义变脏了。这里也是一样。l和v是brdf的特权为什么 D 的归一化要写成∫D(h)(n⋅h)dωh1而不是单纯∫D(h)dωh1因为D 不是“法线方向出现概率”那么简单它更准确地说是单位宏观表面面积上微表面朝向分布的面积密度。这里真正要守恒的是微表面的投影面积不是“方向个数”。如果只是普通概率分布当然很自然会想“所有方向加起来总概率应该是 1所以直接∫ D ( h ) d ω h 1 \int D(h)d\omega_h1∫D(h)dωh1不就好了”但微表面模型不是在数“有多少个法线方向标签”而是在数“朝向h hh的微表面投影回宏观表面后占了多少面积份额。”这就必须乘一个投影项n ⋅ h n\cdot hn⋅h因为一个微表面虽然真实面积一样但它斜着摆时投影到宏观表面法线方向上的面积会变小。这和 Lambert 那套 foreshortening 是同一个几何事实。你可以把它想成很多小瓷砖贴在一个大平面上如果一块小瓷砖正对着宏观法线n nn它的投影面积最大如果一块小瓷砖斜得很厉害它虽然真实面积没变但投影到大平面上的“覆盖份额”更小而宏观表面模型关心的是这些微表面的投影面积加起来能不能正好还原成那块宏观表面。、也就是说这条积分更像“验收标准”。D 的形状从哪里来从表面粗糙统计模型来比如 Beckmann、GGX。这个积分式干什么用来保证这个 D 是一个合法、归一化的 NDF。所以统计模型可以换但是jacobian的brdf部分都是完全相同的处理方式这是你怎么假设表面的微观粗糙统计BeckmannGGX / Trowbridge-Reitz各向同性 / 各向异性甚至别的 NDF这些都在回答小面的法线到底怎么分布
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2444585.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!