【图形系统09】阴影
平面无光照阴影
移动端高性能
只适用于在平面表现的投影
实现步骤
压扁:角色阴影压扁
投影偏移:沿投影方向在地平面($XZ$ 平面)平移
投影拉长:直接平移看不出来阴影拉长的效果,所以还乘以顶点压扁前的 $Y$ 轴高度作为系数进行平移
平面投影阴影平面投影映射不是一个基于图像的解决方案
内容:根据光的方向,把物体的每个顶点投影到平面地面上。数学原理:相似三角形光 Light 所在的点 L 已知,V 已知,P 很容易就能求得
缺点:
只能投影到平面(阴影的接收物只能是平面)
投影物体必须在光线和平面之间
投影阴影平面投影阴影的阴影接收物只能是平面,为了在曲面上得到阴影,做了以下改进:
投影阴影:
把光源当做一个相机/投影器
然后将阴影投影渲染到一张纹理
最后渲染阴影接收者时,将上一步得到的阴影合并进去
投影阴影在 Unity 中的实现
第一步,设置 Project 组件,通过它的参数使用给它的材质生成一个视锥体
第二步,使用 Render Texture 生成一张纹理,将阴影绘制到纹理中
第三步,将设置 Project 组件的物体和阴影纹理进行混合
Sha ...
【图形系统04】纹理
1 纹理映射 TextureMapping首先让我们一起来观察这样一张图:
无论是球上的图案,以及地板的木头纹理都呈现出了不同的颜色信息,那么回想在讲解 Blinn-Phong 反射模型的时候曾提到,一个点的颜色是由其漫反射系数决定的,反射什么颜色的光,人眼就能看见什么颜色。那么针对上面这幅图,难道要去针对每一个点自己去设定一个颜色吗?还是说有什么更方便的方法呢?那便是纹理映射了!
主要有两种方法可以将三维的空间坐标点转化为二维的纹理坐标点:Projector 和 UV Mapping
Projector对于简单的集合体,通常用投影的方式:
倘若拥有从 3 维 World space 到 2 维 Texture space 的一个映射关系,那么只需要将每个点的颜色信息即漫反射系数存储在 2 维的 Texture 之上,每次利用光照模型进行计算的时候根据映射关系就能查到这个点的漫反射系数是多少,所有点计算完之后,结果就像最左边的 screen space 之中,整个 Texture 被贴在了模型之上。
一个纹理坐标使用的伪代码供参考:
简而言之就是对每个光栅化的屏幕坐标算出它的 uv ...
【图形系统10】色彩
色彩与色彩空间理论略
1 伽马矫正Gamma 校正Gamma 是指对线性 rgb 值和非线性视频信号之间进行编码和解码的操作。
颜色空间
图中可以看到,sRGB 和 Rec. 709 的色域是差不多的,三原色的位置是相同的,那么它们之间的区别就是:传递函数不同
传递函数
什么是传递函数:知道了颜色的颜色值之后,想要在电子设备上显示,就需要把它转换为视频信号,传递函数就是用来做转换的。
一个传递函数包括两部分:
光转电传递函数(OETF),把场景线性光转到非线性视频信号值。
电转光传递函数(EOTF),把非线性视频信号值转到显示光亮度
一个简单理解:拍照时,将照片存储在内存卡中,就是用视频信号存储的,如果要看这个照片,就把视频信号再转换成光信号。传递函数其实就是 Gamma 校正所使用的函数。
简单定义线性空间(Linear Space)是 RGB 颜色都处于函数曲线的对角直线状态(下图),处于线性空间的颜色值是线性变化的,而不会有任何非线性改变,用公式来表达是:$x’=f (x) = x$。
与线性空间相对立的是 Gamma 空间,它是有着指数级的函数曲 ...
【图形系统07】混合和剔除
混合指令混合指令以 Blend 关键词开始,后面接混合模式。它可以在 Subshader 中使用,也可以在 Pass 中使用。
在 SubShader 中使用会影响到 SubShader 中的所有 pass
在 Pass 中使用只会对当前 Pass 起作用。
混合命令开启后,会禁用 GPU 上的一些优化(主要是隐藏表面/去除 Early-Z),这会使GPU 帧时间增加。
默认操作符是 Add。
单独的 RGB 和 Alpha 混合与高级 OpenGL 混合操作不兼容。
[!NOTE] 参数解释
Src: 该片元产生的颜色 Source(源颜色)
Dst: 已经存在于颜色缓存的颜色被称为 Destination(目标颜色)
SrcFactor :源颜色的混合因子
Dst Factor :目标颜色的混合因子
可用的混合模式如下:
Blend Off:关闭混合处理,当 Shader 中没有添加任何混合指令的时候,默认就是关闭状态。
BlendSrcFactor DstFactor:开启混合处理,允许自定义混合模式。源颜色和目标颜色按照如下公式进行颜色混合: ...
HLSL ShaderLab
ShaderLab 语法基础1 组织结构Shader 中可以编写多个子着色器(SubShader),但至少需要一个。
在应用程序运行过程中,GPU 会先检测第一个子着色器能否正常运行,如果不能正常运行就会再检测第二个,以此类推。假如当前 GPU 的硬件版本太旧,以至于所有的子着色器都无法正常运行时,则执行最后的回退(Fallback)命令,运行指定的一个基础着色器。
如果编写的是顶点-片元着色器(Vertex-Fragment Shader),每个子着色器中还会包含一个甚至多个 Pass。在运行的过程中,如果某个子着色器能够在当前 GPU 上运行,那么该子着色器内的所有 Pass 会依次执行,每个 Pass 的输出的结果会以指定的方式与上一步的结果进行混合,最终输出。
如果编写的是表面着色器(Surface Shader),着色器的代码也是包含在子着色器中,但是与顶点-片元着色器不同的是,表面着色器不会再嵌套 Pass。系统在编译表面着色器的时候会自动生成多个对应的 Pass,最终编译出来的 Shader 本质上就是顶点-片元着色器。
2 名称Shader 程序的第一行代码用来声明该 ...
Effective C++
零、导读术语声明式declaration声明式:告诉编译器某个东西的名称和类型。
123456789101112//对象声明式extern int x;//函数声明式std::size_t NumDigits(int number);//类声明式class Widget;//模板声明式template <typename T>class GraphNode;
注意作者在这里把看基本类型看作对象(Obejct)。顺带一提,size_t 本质是 unsigned int,是一个 typedef
函数签名式signature函数的声明揭示了其签名式。也就是参数和返回值。一个函数的签名等同于该函数的类型。C++ 对于签名的官方定义中并不包含函数的返回类型,这意味着函数的重载不能根据返回值判断。(本书把返回类型视为签名的一部分)
123456789void Fun() { }int Fun() //error:has already been declared with return type 'void'. { r ...
UE实时渲染
渲染前准备
第0帧-0毫秒–游戏线程(CPU)在开始渲染前,我们需要知道参与渲染的物体在什么位置
游戏线程执行所有逻辑计算和物体的变换
动画
模型或物体的位置物理
Al
生成或销毁,隐藏或显示
以及其他任何与物体位置变化相关的逻辑
第1帧-33 ms-渲染线程(大部分在CPU)至此,我们已经知道了各个物体处于什么位置我们还需要知道哪些物体应该被渲染在画面中 → 只渲染可见的内容部分也是在CPU上计算的,但也有部分是在GPU上计算
遮挡过程—建立一个可见物体的列表((逐物体而不是逐三角形)分为以下4个步骤处理
距离剔除:根据物体与相机的距离决定是否剔除(默认未开启)
视锥剔除:根据物体是否在视锥内决定是否剔除
预计算可见性:将可见性结果提前计算好并储存下来
遮挡剔除:性能消耗最大,在最后才执行遮挡过程性能提示
设置距离剔除
大于1万个物体会对性能有较大影响
大部分情况下CPU是瓶颈,有时候也可能是遮挡在大型开放世界通常作用比较小
所有的物体都会被遮挡
大的物体很难被遮挡,因此在GPU渲染上可能需要消耗更多时间
几何体渲染第2帧-66 ms(GPU)Prepass/E ...
UE输入系统
增强输入Enhanced Input System 是对默认输入系统做了一个扩展,它以模块化的方式解耦了从输入的按键配置到事件处理的逻辑处理过程,提供了更灵活和更便利的输入配置和处理功能。同时又能向后兼容虚幻引擎 4 (UE4)中的默认输入系统。以插件的形式供我们使用。
重要的类:
UInputAction 输入操作,简称IA
是交互角色可能做出的任何动作,独立于原始输入,可以设置多种类型的输入值
UInputMappingContext 输入映射上下文, 简称IMC
将用户的输入映射到操作,并可以动态地为每个用户添加、移除或安排优先次序。通过**本地玩家子系统UEnhancedInputLocalPlayerSubsystem将一个或多个映射应用到本地玩家,并安排它们的优先次序,避免多个操作由于尝试使用同一输入而发生冲突
UInputModifier 输入修饰符。简称IM
调整来自用户设备的原始输入的值,如使用 Negate 可以将输入值取负值。
UInputTrigeer 输入触发器。简称 IT
根据特定的条件来确定是否应该激活输入操作。
增强输入系统优点
统 ...
UEC++定时器
定时器管理定时器在 全局定时器管理器(FTimerManager 类型)中管理。全局定时器管理器存在于 游戏实例 对象上以及每个 场景 中。如果要对其调用定时器的对象(如 Actor)在时间结束前被销毁,则相关定时器会自动取消。在此情况下,定时器句柄将变为无效,并且不会调用该函数。
定时器句柄(**FTimerHandle** 类):可用于暂停(和恢复)倒计时,查询或更改剩余时间,甚至可以取消定时器。
访问定时器管理器:可以使用 AActor 函数 **GetWorldTimerManager()**,它会在 UWorld 中调用 GetTimerManager() 函数,来访问 FTimerManager。
访问全局定时器管理器:使用 UGameInstance 函数 GetTimerManager()。如果场景因为任何原因而没有自己的定时器管理器,也可以退而求其次,使用全局定时器管理器。全局管理器可以用于与任何特定场景的存在没有相关性或依赖性的函数调用。
使用定时器管理器来设置定时器:SetTimer 和 SetTimerForNextTick,它们各自都有一些重载。每个函数都可 ...
UEC++基础API
虚幻类UObject创建 Object
UObject 构造函数不支持参数。所有的 C++ UObject 都会在引擎启动的时候初始化,然后引擎会调用其默认构造器。如果没有默认的构造器,那么 UObject 将不会编译。
UObject 构造函数应该轻量化,仅用于设置默认的数值和子对象,构造时不应该调用其它功能和函数。
UObject 永远都不应使用 new 运算符。 所有的 UObjects 都由虚幻引擎管理内存和垃圾回收。如果通过 new 或者 delete 手动管理内存,可能会导致内存出错。
NewObject<T> :创建一个 UObject 实例,仅在运行时使用
CreateDefaultSubobject<T> :创建一个组件或者子对象,在构造函数中使用
例子:创建静态网格体
123456789101112131415161718// 构造函数中// 创建网格体组件,以便查看球体位置 UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshCompo ...