一点心得


  • 首页

  • 归档

Unity的MemoryPool模块

发表于 2018-07-14

unity 4.7.1f1

MemoryPool主要是对内存分配的碎片化和频繁分配和释放进行优化。

结构

MemoryPool的设计结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MemoryPool
{
    ...
  
private:
    struct Bubble
    {
        char data[1]; //只是一个占位符,其实这个成员变量加不加sizeof(Block)大小都是1个字节
    };

    typedef dynamic_array<Bubble*> Bubbles;

    Bubble m_Bubbles;
  
    void* m_HeadOfFreeList;
  
    ...
}
阅读全文 »

Unity Shaders and Effects Cookbook总结

发表于 2018-04-03

Phong和BlinnPhong反射类型光照模型

Phong

Phong模型是基于光照(图中1 Light向量)的方向和用户的视角(图1 View向量)方向进行计算的。通过计算Light的反射向量与用户的视角方向向量的向量积作为光照的强度因子。

计算公式:

代码方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//lightDir就是图1的Light向量,viewDir就是图1的View向量
fixed4 LightingPhong(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
	//Reflection
  	float NdotL = dot(s.Normal - lightDir);
	float3 reflectionVector = normalize(2.0 * Normal * NdotL - Light);
    
  	//Specular
  	float spec = pow(max(0, dot(reflectionVector, viewDir)), _SpecPower);
  	float3 finalSpec = _SpecularColor.rgb * spec;
  
  	fixed4 color;
  	color.rgb = (s.Albedo * _LightColor0.rgb * max(0, NdotL) * atten) + (_LightColor0.rgb * finalSpec);
  	color.a = s.Alpha;
  
  	return color;
}

BlinnPhong

BlinnPhong可以看作是堆Phong模型的简化版,它是通过视角方向(View)和光照方向(Light)构成的半角向量(Half)来计算光照的。直接使用半角向量而不用计算光照的反射向量的方式更加高效。

计算公式:

代码方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//lightDir就是图1的Light向量,viewDir就是图1的View向量
fixed4 LightingPhong(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
  	float NdotL = dot(s.Normal - lightDir);
  
    float3 halfVector = normalize(lightDir + viewDir);
  	float NdotH = max(0, dot(s.Normal, halfVector));
  	float spec = pow(NdotH, _SpecPower), _SpecularColor);
  
  	fixed4 color;
  	color.rgb = (s.Albedo * _LightColor0.rgb * max(0, NdotL) * atten) + (_LightColor0.rgb * finalSpec);
  	color.a = s.Alpha;
  
  	return color;
}

挤压效果

这种挤压的效果的原理是将顶点沿发现方向进行投影,用代码表示就是这样:

1
v.vertex.xyz += v.normal * _Amount;

_Amount 是挤压的因子

还可以通过额外添加一个纹理(或者使用主要纹理的alpha通道)来表示挤压的程度:

1
2
3
4
5
6
7
8
sampler2D _ExtrusionTex;
void vert(inout appdata_full v)
{
	float4 tex = tex2Dlod(_ExtrusionTex, float4(v.texcoord.xy, 0, 0));;
	float extrusion = UnpackNormal(tex.r);
	
	v.vertex.xyz += v.normal * _Amount * extrusion;
}

从顶点修改器中采样一个纹理,应该使用texDlod而不是tex2D。

抓取功能

原理是结合vertex和fragment着色器以及抓取通行技术,然后对抓取纹理进行采样,在对其UV值做一点修改来制作出一些细微的变形效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Pass {
  #pragma vertex vert
  #pragma fragment frag
  #include "UnityCG.cginc"
  
  //抓取通行会自动创建一个纹理,然后通过这个变量可以引用到
  sampler2D _GrabTexture;
  
  struct vertInput {
    float4 vertex : POSITION;
  };
  
  struct vertOutput {
    float4 vertex : POSITION;
    float4 uvgrab : TEXCOORD1;
  };
  
  vertInput vert(vertexInput v) {
    vertexOutput o;
    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    //获取到抓取的UV
    o.uvgrab = ComputeGrabScreenPos(o.vertex);
    return o;
  }
  
  half4 frag(vertexOutput i) : COLOR {
    //通过抓取的UV来采样抓取纹理的值
    fixed4 col = tex2Dproj(_GrabTexture, Unity_PROJ_COORD(i.uvgrab));
    return col + half4(0.5, 0, 0, 0);
  }
}
阅读全文 »

Unity渲染流程简述

发表于 2018-01-14

unity 4.7.1f1

之前了解Unity的setpass call和batches时把顺道把渲染这块的代码(4.7.f1)也阅读了下,先利用一个思维导图把渲染的几个大的步骤记下来,以便有个整体的脉络(这里只记录Forward渲染)。

注1:由Camera的投影矩阵和对象的Z轴向量的积得出

阅读全文 »

UGUI的Graphic Rebuild问题

发表于 2017-12-09

今天在项目中发现了一个UI的性能消耗比较异常的问题。在Unity的Profiler里面我看到一项Canvas.SendWillRenderCanvasesCPU消耗持续比较高,但是查看游戏的UI界面好像动态变化的UI并没有。在逐个关闭UI才定位到有一张图片一直在做渐变效果,所以才导致UI一直在重建。这里写个简单的例子模拟下,在场景中创建一张Image,然后挂载一个测试脚本,在这个脚本里面一直更新这个Image的alpha通道就可以看到这种现象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestGraphicRebuild : MonoBehaviour
{
    public Image FadeImage;
    private float _mAlpha = 0;

	void Update ()
	{
	    _mAlpha += 0.01f;
	    _mAlpha = _mAlpha > 1 ? 0 : _mAlpha;
	    
        FadeImage.color = new Color(
                              FadeImage.color.r, 
                              FadeImage.color.g, 
                              FadeImage.color.b, 
                              _mAlpha);
	}
}
阅读全文 »

C# 泛型集合的Boxing问题

发表于 2017-12-05

今天检查项目中代码的Boxing问题的时候。有一个点当时让我困惑了不少时间。如下:

1
2
3
4
5
6
7
8
9
public IEnumerator<float> Func0()
{
  yield return 0.1f;
}

public IEnumerator Func1()
{
  yield return 0;
}

这里在实际代码运行过程中Func1会产生Boxing而Func0没有产生Boxing,按照自己浏览的C#的文档对Boxing的理解这里应该都是会产生Boxing的才对。下面是C#的Boxing文档说明的:

Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type. When the CLR boxes a value type, it wraps the value inside a System.Object and stores it on the managed heap.

查看了IEnumerator的定义:

1
2
3
4
public interface IEnumerator<T> : IEnumerator, IDisposable
{
  T Current {get;}
}

是interface类型,那么上面的函数中yield return 0.1f中的0.1f这个float类型的变量转换为IEnumerator为什么不会Boxing呢? 先查看下两个方法的IL代码,对比下IL代码的区别。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//对于Func0
.class nested private auto ansi sealed beforefieldinit '<Func0>c__Iterator0'
	...
{
	...
	// Fields
	.field assembly float32 $current //current编译成为了我们指定的float类型
	.field assembly bool $disposing
	.field assembly int32 $PC	
	...
}

//对于Func1
.class nested private auto ansi sealed beforefieldinit '<Func1>c__Iterator1'
	...
{
	...
	// Fields
	.field assembly object $current//current编译成为了默认的object类型
	.field assembly bool $disposing
	.field assembly int32 $PC
}

那么问题就很清楚了,Func0其实每次迭代的时候接收值的变量就是float类型(对应于IL的float32类型)的所以根本不需要转换类型。但是对于Func1函数,编译成IL代码存储当前值的变量是object类型的,所以当我们Func1中返回值为0的int类型时,这个int型的变量会被转换成object类型而导致Boxing。

其实正确的操作是先看C#关于泛型的文档才对。不过现在看下也不迟。其中第一段就有如下描述:

by using a generic type parameter T you can write a single class that other client code can use without incurring the cost or risk of runtime casts or boxing operations

到这儿疑问就比较明确的解决了。

阅读全文 »
1 … 4 5 6 … 9
© 2025 yiliangduan@qq.com