Unity3D如何將圖片以正確的像素顯示在屏幕上

文章要解決的問題在於:我有一張圖片,我在Unity中如何按照正常的製作尺寸顯示在正交相機上。或者說,我如何讓圖片按照1像素對應1屏幕解析度的顯示在屏幕上。

理論基礎

在正交相機中唯一與顯示範圍相關的屬性只有一個,那就是 Size,單位為 Unity 單位(1單位的世界坐標)。這個屬性的值代表了攝像機在縱向上一半的顯示範圍。舉例來說,如果把 Size 設置為5,那就意味著這個攝像機在縱向可以顯示10個 Uinty 單位。攝像機的 Size 是不會隨著屏幕的解析度而變化。而攝像機橫向的顯示範圍則是會發生變化的,Uinty 3D 通過屏幕的寬度除以高度獲 Camera.aspect,再通過縱向的顯示範圍乘以 Camera.aspect 獲得攝像機橫向的顯示範圍,這就保證了攝像機的顯示範圍可以覆蓋整個屏幕。

一般情況下,我們都會把正交相機的 Size設置為1,這時候一屏幕的高度就對應2個Unity單位的世界坐標,假設屏幕的解析度是 1920*1080 的話,那麼1像素對應的世界坐標等於2/1080。

從上我們導出一個像素轉換世界坐標長度的數學公式

worldSize=pixSize	imes(2camSize div screenHeight)

一般情況下,我們喜歡按照像素的習慣去操作坐標(尤其是做仿射動畫),所以一般會把所有的2D圖片顯示在一個 以 2camSizediv screenHeight 縮小好的根節點之下,這樣 localSize 就等於 pixSize

上機實驗

說好理論基礎知識我們就開始上代碼吧,先給自己明確好需求:將圖1以正常的製作尺寸顯示在camSize為1的正交相機上。

圖1

第一步,設置好我們的正交相機屬性

第二步,編寫腳本,一共兩個腳本。

第一個腳本綁在根節點(本文直接拿相機節點當根節點),用來控制根節點的縮放比例,在編輯器下,點擊下Menu菜單,設置好根節點縮放比例應該就可以刪了。

using System;using System.Collections;using UnityEngine;public class CamSize : MonoBehaviour { public float screenHeight = 1080; [ContextMenu("Set Root Radio")] void SetRootRadio() { Camera cam = GetComponent<Camera>(); if (cam == null) throw new NullReferenceException("this componet must attach to a gameObject has camera component"); float radio = 2 * cam.orthographicSize / screenHeight; transform.localScale = Vector3.one * radio; }}

第二個腳本綁在顯示節點上,用來構造顯示的MeshRender數據,然後加上渲染材質球,我們的Miku就顯示在相機上了。

using UnityEngine;using System.Collections;public class ShowPicture : MonoBehaviour { #region member public Material spriteMaterial; private int m_verticesCount = 4; #endregion // Use this for initialization void Start() { initSprite(); } //根據寬高生成對應的面// private void initSprite() { //獲取圖片的像素寬高// int pixelHeight = spriteMaterial.mainTexture.height; int pixelWidth = spriteMaterial.mainTexture.width; Debug.Log("pixeW:" + pixelWidth + ",pixeH:" + pixelHeight); //得到MeshFilter對象// MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>(); if (meshFilter == null) { //為null時,自動添加// meshFilter = gameObject.AddComponent<MeshFilter>(); MeshRenderer meshRenderer = gameObject.AddComponent<MeshRenderer>(); meshRenderer.sharedMaterial = spriteMaterial; } //得到對應的網格對象// Mesh mesh = meshFilter.mesh; //三角形頂點的坐標數組// Vector3[] vertices = new Vector3[m_verticesCount]; //三角形頂點數組// int[] triangles = new int[m_verticesCount * 3]; float glWidth = pixelWidth / 2; float glHeight = pixelHeight / 2; //以當前對象的中心坐標為標準// vertices[0] = new Vector3(-glWidth, -glHeight, 0); vertices[1] = new Vector3(-glWidth, glHeight, 0); vertices[2] = new Vector3(glWidth, -glHeight, 0); vertices[3] = new Vector3(glWidth, glHeight, 0); mesh.vertices = vertices; //綁定頂點順序// triangles[0] = 0; triangles[1] = 1; triangles[2] = 2; triangles[3] = 1; triangles[4] = 3; triangles[5] = 2; mesh.triangles = triangles; mesh.uv = new Vector2[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(1, 1) }; }}

然後貼上Sprite的顯示Shader,我是直接 copy NGUI的sprite Shader的

Shader "Unlit/Transparent Colored"{ Properties { _MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {} } SubShader { LOD 200 Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Pass { Cull Off Lighting Off ZWrite On Fog { Mode Off } Offset -1, -1 Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; float4 _MainTex_ST; struct appdata_t { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; fixed4 color : COLOR; }; struct v2f { float4 vertex : SV_POSITION; half2 texcoord : TEXCOORD0; fixed4 color : COLOR; }; v2f o; v2f vert (appdata_t v) { o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.texcoord = v.texcoord; o.color = v.color; return o; } fixed4 frag (v2f IN) : COLOR { half4 col = tex2D(_MainTex, IN.texcoord); if(dot(IN.color, fixed4(1,1,1,0)) <= 0.02) { col.rgb = dot(col.rgb, fixed3(.222,.707,.071)); } else { col = col * IN.color; } return col; } ENDCG } } SubShader { LOD 100 Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } Pass { Cull Off Lighting Off ZWrite Off Fog { Mode Off } Offset -1, -1 ColorMask RGB AlphaTest Greater .01 Blend SrcAlpha OneMinusSrcAlpha ColorMaterial AmbientAndDiffuse SetTexture [_MainTex] { Combine Texture * Primary } } }}

最後把編輯器的顯示長寬比例設置為1920*1080,我們就可以一直全屏的顯示我們的 Miku了。

最後貼上我的,Unity 工程地址

ElPsyCongree/show_picture

推薦閱讀:

趁熱度來做個捏臉
GAN在2017年實現四大突破,未來可能對計算機圖形學產生衝擊
計算機圖形學常用術語整理
【GPU精粹與Shader編程】(一) 開篇 & 全系列11本書核心知識點總覽
Substance Designer 中實現形態圖形處理演算法(II) --- 非均勻的腐蝕拓展

TAG:計算機圖形學 |