目录
1.将摄像机定位到模型实际中心点前边(防止有些模型中心点和实际模型中心位置偏移很大的情况)
2.获取当鼠标在RawImage上时,鼠标位置对应的图像坐标(简单粗暴方式)
3.设置脚本运行顺序
4.当plugins底下出现dll文件识别不到的情况(主要出现在2018版本)
5.[优化]Unity中的阴影优化方案
1.无阴影
2.脚底假阴影
3.Unity系统的实时阴影
4.软阴影
6.关于MaterialPropertyBlock
7.关于拖动文件到Unity主窗口的相关方法
8.快速让物体生成镜像的做法(面试问过这个)
9.在 OnSceneGUI and OnInspectorGUI 方法之间共享变量
10.设置cullingMask为Everything的简单方式
11.前向渲染路径中的一些细节(Forward Rendering)
12.关于Application.targetFrameRate
13.为什么不能直接更改transform.position.x的值?
14.自动检测UI内容是否显示全:content size filter控件
15.光照计算中的diffuse的计算公式
16.法线贴图的原理,以及它为什么偏淡蓝色
17.记录一个个人使用比较好用的相机控制脚本
1.将摄像机定位到模型实际中心点前边(防止有些模型中心点和实际模型中心位置偏移很大的情况)
public static void FitToBounds(this Camera camera, Transform transform, float distance)
{
var bounds = transform.EncapsulateBounds();
var boundRadius = bounds.extents.magnitude;
//根据摄像机视角计算摄像机位置
var finalDistance = boundRadius/(2.0f*Mathf.Tan(0.5f*camera.fieldOfView*Mathf.Deg2Rad))*distance;
if (float.IsNaN(finalDistance))
return;
camera.transform.position = new Vector3(bounds.center.x, bounds.center.y, bounds.center.z + finalDistance);
camera.transform.LookAt(bounds.center);
}
public static Bounds EncapsulateBounds(this Transform transform)
{
Bounds bounds;
var renderers = transform.GetComponentsInChildren<Renderer>();
if (renderers != null && renderers.Length > 0)
{
bounds = renderers[0]. bounds;
for (var i = 1; i < renderers.Length; i++)
{
var renderer = renderers[i];
bounds.Encapsulate(renderer.bounds);
}
} else
{
bounds = new Bounds();
}
return bounds;
}
2.获取当鼠标在RawImage上时,鼠标位置对应的图像坐标(简单粗暴方式)
Vector3[] corners = new Vector3[4];
GetComponent<RawImage>().rectTransform.GetWorldCorners(corners);
Rect newRect = new Rect(corners[0], corners[2]-corners[0]);
Debug.Log(newRect.Contains(Input.mousePosition));
3.设置脚本运行顺序
[DefaultExecutionOrder(int order)]
order值越小运行顺序越靠前(一般想要某个脚本最先运行要设置一个负值)
4.当plugins底下出现dll文件识别不到的情况(主要出现在2018版本)
public class MyPluginClass
{
//
static MyPluginClass()
{
String currentPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process);
String dllPath = Environment.CurrentDirectory + Path.DirectorySeparatorChar + "Assets" + Path.DirectorySeparatorChar + "Plugins";
if(currentPath.Contains(dllPath) == false)
{
Environment.SetEnvironmentVariable("PATH", currentPath + Path.PathSeparator + dllPath, EnvironmentVariableTarget.Process);
}
}
}
5.[优化]Unity中的阴影优化方案
1.无阴影
对于一些重要性很低或者很难被注意到的,当然大多数情况下是硬件设备配置达不到要求而采用低配画质设置的,这种情况下直接不产生任何阴影,虽然这看起来会让玩家觉得不那么真实,但是为了能让低端机上能够保持一定的帧率运行,这样是不可避免的
2.脚底假阴影
fake_shadow,脚底的阴影贴图,为所有需要添加假阴影的人物,脚底添加假阴影预制体,实时跟随人物移动,开销只是一张贴图的开销,但不太适用于移动的NPC或者怪物,毕竟游戏中地形高低起伏,一张贴图可能会产生穿帮,所以为静止的NPC使用这种较为合适
3.Unity系统的实时阴影
在有光源并勾选了Cast Shadow的情况下,物体会投射阴影,勾选了Receive Shadow的物体表面会接收到投射的阴影,由于阴影是实时的,一般移动的物体使用这个效果会比较好,但同时这个开销也是不可避免的。unity自带的shadowmap阴影基于深度,需要浮点纹理的支持,但是有的移动平台不支持,而且开销很大。
shadowMap的阴影实现原理:将相机放在和光源重合的位置上,那么场景中相机看不到的地方就是光源的阴影区域,然后通过相机将这个阴影映射纹理存储,可以每次采样得到某个方向距离光源最近的点(比这个点远则说明在阴影中)
4.软阴影
何为软阴影,游戏引擎中的阴影为实时的数学计算,投射出的阴影都是有棱有角,在阴影和非阴影的边界也是区分明显,而真实世界中则不是这样,真实世界中的阴影收到间接光照等的影响,在阴影边缘形状往往不那么锋利,而且往往过渡平滑。那么如何在引擎中实现这一技术,近几年大火的实时光照追踪技术,即将一束光的第一次,第二次甚至更多反射的影响都计算上去,得到更为真实的渲染效果,不过这一过程计算复杂,开销往往也比较大。
6.关于MaterialPropertyBlock
使用Unity3D做开发的朋友应该都知道,对于实例化出来的模型,我们改变它身上的颜色值或者贴图之类,Unity是会把它当前使用的ShareMaterial复制一份实例出来,以做到不同对象身上的材质互不影响的改变参数。但这样做会导致如果使用的对象很多,就会产生很多材质的实例的问题,这样会对GC有一定的消耗。但是如果使用MaterialPropertyBlock,没有生成任何的Material的实例出来:
void ChangeColor()
{
if (rootObj == null)
{
return;
}
Transform[] ts = rootObj.GetComponentsInChildren<Transform>();
objects = new GameObject[ts.Length];
for (int i = 0; i < ts.Length; i++)
{
objects[i] = ts[i].gameObject;
}
MaterialPropertyBlock props = new MaterialPropertyBlock();
MeshRenderer renderer;
foreach (var obj in objects)
{
float r = Random.Range(0.0f, 1.0f);
float g = Random.Range(0.0f, 1.0f);
float b = Random.Range(0.0f, 1.0f);
props.SetColor("_Color",new Color(r,g,b));
renderer = obj.GetComponent<MeshRenderer>();
if (renderer == null)
{
continue;
}
renderer.SetPropertyBlock(props);
}
}
7.关于拖动文件到Unity主窗口的相关方法
https://github.com/Bunny83/UnityWindowsFileDrag-Drop
8.快速让物体生成镜像的做法(面试问过这个)
调整scale为 "原scale值 * (-1)" 即可。
9.在 OnSceneGUI and OnInspectorGUI 方法之间共享变量
之前这两个方法可以共享全局变量,但是突然在某一个版本没用了。现在需要在全局变量其那边加Static标识才行。
10.设置cullingMask为Everything的简单方式
camera.cullingMask = -1; // -1 means "Everything"
11.前向渲染路径中的一些细节(Forward Rendering)
在 Forward Rendering 中作用在每个物体上一定数量最亮的光,被渲染在全逐像素(per-pixel)光照模式下。然后最多4个点光源会被逐顶点(per-vertex)计算。其他的光照被作为 Spherical Harmonics (SH) (球谐光照)计算, 它很快但是只是一个近似值。一个光源是否为一个逐像素光照取决于下:
- 光源的 Render Mode 被设置为 Not Important 永远是 per-vertex or SH.
- 最亮的平行光永远是 per-pixel.
- 渲染模式被设置为 Important 的光永远是per-pixel。
- 如果符合上述条件的光的数量,小于当前 Pixel Light Count Quality Setting,那么更多的光会被渲染为 per-pixel,根据光照亮度的顺序由高到低。
12.关于Application.targetFrameRate
1.使用VR设备时,会使用VR SDK所指定的相应帧率,会忽略掉游戏中指定的值。(设置无效)
2.当设置了vSyncCount属性(垂直同步),将使用vSyncCount和平台的默认渲染率来确定目标帧率,会忽略targetFrameRate。例如,如果平台的默认渲染速度是每秒60帧,而vSyncCount被设置为2,那么游戏的目标就是每秒30帧。
13.为什么不能直接更改transform.position.x的值?
这是c#中的结构体当作返回值的问题,结构体是值类型,而值传递是复制传递,所以返回的值不是原来的值了,对此返回值的修改是无效的,所以编译器就从源头上禁止了这样的操作。
14.自动检测UI内容是否显示全:content size filter控件
15.光照计算中的diffuse的计算公式
diffuse = Kd * lightColor * max(dot(N, L), 0)
其中:
Kd是材质的漫反射颜色。
lightColor是入射漫反射光的颜色。
N是规范化的表面法向量。
L是规范化的从顶点到光源的向量。
16.法线贴图的原理,以及它为什么偏淡蓝色
逐像素计算光照时,我们每一个像素都会根据该点的法向量来计算最终该点的光照结果,那么,我们如果能够改变这个法线的方向,不是就可以改变这个点的光照结果了呢!那么,把纹理采样的思想用在这里,我们直接用一张图来存储法线,逐像素计算时,在采样diffuse贴图的时候,再采样一张法线的贴图,就可以修改法线了,进而修改最终的效果。
既然我们知道了法线贴图中存储的是切线空间的法线。而法线贴图所对应的表面,绝大部分的位置肯定是平滑的,只有需要凹凸变化的地方才会有变化,那么大部分地方的法线方向不变,也就是在切线空间的(0,0,1),这个值按照上面介绍的映射关系,从(-1,1)区间变换到(0,1)区间:(0*0.5+0.5,0*0.5+0.5,1*0.5+0.5)= (0.5,0.5,1),再转化为颜色的(0,255)区间,最终就变成了(127,127,255)。好了,打开photoshop,看一下这个颜色值是 不是偏向蓝色。
17.记录一个个人使用比较好用的相机控制脚本
using System.Collections;
using System.Collections.Generic;
using TriLib.Samples;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class CameraController : MonoBehaviour
{
public bool AllowMouseScrollWheel;
[SerializeField]
private AssetLoaderWindow AssetLoader;
private float oriOffsetDistance;
public KeyCode moveForwardKey;
public KeyCode moveBackKey;
public KeyCode moveLeftKey;
public KeyCode moveRightKey;
public KeyCode moveUpKey;
public KeyCode moveDownKey;
public float distance = 5.0f;
public float maxDistanceToDevice;
public float minDistanceToDevice;
public float xSpeed = 200.0f;
public float ySpeed = 200.0f;
public int yMinLimit = -80;
public int yMaxLimit = 80;
public int zoomRate = 40;
public float zoomDampening = 5.0f;
public Transform mainCameraTrans;
public GameObject TargetGameObject;
private float mouse_x;
private float mouse_y;
private float xDeg = 0.0f;
private float yDeg = 0.0f;
private float currentDistance;
private float desiredDistance;
private Quaternion currentRotation;
private Quaternion desiredRotation;
private Quaternion rotation;
private Vector3 position;
private bool dragging = false;
private float pinchdistance = 0;
private EventSystem eventsystem;
// Start is called before the first frame update
void Start()
{
mainCameraTrans = this.GetComponent<Camera>().transform;
eventsystem = FindObjectOfType<EventSystem>() as EventSystem;;
}
public void InitCamera(GameObject targetGameObject)
{
distance = Vector3.Distance(transform.position, targetGameObject.transform.position);
currentDistance = distance;
desiredDistance = distance;
//be sure to grab the current rotations as starting points.
position = transform.position;
rotation = transform.rotation;
currentRotation = transform.rotation;
desiredRotation = transform.rotation;
Vector3 cross = Vector3.Cross(Vector3.right, transform.right);
xDeg = Vector3.Angle(Vector3.right, transform.right);
if (cross.y < 0) xDeg = 360 - xDeg;
yDeg = Vector3.Angle(Vector3.up, transform.up);
}
// Update is called once per frame
void LateUpdate()
{
TargetGameObject = AssetLoader.CenterPointObj;
//CheckOffsetCameraMouseRay();
if (AllowMouseScrollWheel&&DropToWindow.Instance.IsCompleteLoaded)
{
float moveAmountX = xSpeed * Time.deltaTime;
float moveAmountY = ySpeed * Time.deltaTime;
// CheckOffsetCameraMouseRay();
//if (EventSystem.current.IsPointerOverGameObject())
// return;
if (Input.GetKey(moveForwardKey))
{
if (!CheckIfLessThanDistance())
mainCameraTrans.position += mainCameraTrans.forward * moveAmountX;
}
else if (Input.GetKey(moveBackKey))
{
if (!CheckIfMoreThanDistance())
mainCameraTrans.position -= mainCameraTrans.forward * moveAmountX;
}
else if (Input.GetKey(moveLeftKey))
{
if (!CheckIfMoreThanDistance())
mainCameraTrans.position -= mainCameraTrans.right * moveAmountX;
}
else if (Input.GetKey(moveRightKey))
{
if (!CheckIfMoreThanDistance())
mainCameraTrans.position += mainCameraTrans.right * moveAmountX;
}
else if (Input.GetKey(moveUpKey))
{
if (!CheckIfMoreThanDistance())
mainCameraTrans.position += mainCameraTrans.up * moveAmountY;
}
else if (Input.GetKey(moveDownKey))
{
if (!CheckIfMoreThanDistance())
mainCameraTrans.position -= mainCameraTrans.up * moveAmountY;
}
bool outsideNewGUI = true;
if (eventsystem) outsideNewGUI = !eventsystem.IsPointerOverGameObject();
if (Input.GetMouseButton(1))
{
if (!Input.GetMouseButtonDown(1))
{
xDeg += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
yDeg -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
OrbitAngle
//Clamp the vertical axis for the orbit
yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
// set camera rotation
desiredRotation = Quaternion.Euler(yDeg, xDeg, 0);
//currentRotation = transform.rotation;
//rotation = Quaternion.Lerp(currentRotation, desiredRotation, Time.deltaTime * zoomDampening);
//transform.rotation = rotation;
}
}
currentRotation = transform.rotation;
rotation = Quaternion.Lerp(currentRotation, desiredRotation, Time.deltaTime * zoomDampening);
transform.rotation = rotation;
float scrollinp = Input.GetAxis("Mouse ScrollWheel");
desiredDistance -= scrollinp * Time.deltaTime * zoomRate * Mathf.Abs(desiredDistance);
//clamp the zoom min/max
desiredDistance = Mathf.Clamp(desiredDistance, minDistanceToDevice, maxDistanceToDevice);
// For smoothing of the zoom, lerp distance
currentDistance = Mathf.Lerp(currentDistance, desiredDistance, Time.deltaTime * zoomDampening);
//currentRotation = transform.rotation;
//rotation = Quaternion.Lerp(currentRotation, desiredRotation, Time.deltaTime * zoomDampening);
//transform.rotation = rotation;
// calculate position based on the new currentDistance
position = TargetGameObject.transform.position - (rotation * Vector3.forward * currentDistance );
transform.position = position;
}
}
bool CheckIfMoreThanDistance()
{
//if (mainCameraTrans == null)
// mainCameraTrans = Camera.main.transform;
GameObject t = AssetLoader.CenterPointObj;
if (t == null)
return false;
if (Vector3.Distance(mainCameraTrans.position,
t.transform.position) > maxDistanceToDevice)
return true;
return false;
}
bool CheckIfLessThanDistance()
{
GameObject t = AssetLoader.CenterPointObj;
if (t == null)
return false;
//if (mainCameraTrans == null)
// mainCameraTrans = Camera.main.transform;
if (Vector3.Distance(mainCameraTrans.position,
t.transform.position) < minDistanceToDevice)
return true;
return false;
}
private static float ClampAngle(float angle, float min, float max)
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp(angle, min, max);
}
}