Unity Shader学习笔记(一)坐标变换

基本问题

  试想我们的美术做了一个3D模型,然后渲染引擎把模型渲染到屏幕上。我们还可以选定不同的视角,模拟不同的光照条件去观察模型。现在来分析一下这个过程。如果说我们把这个过程看成一个函数,那么函数的输出就是屏幕上的图像。确切地说,是屏幕上的每个像素。这个函数的主要输入是这个3D模型,我们观察的视角,光照情况等等因素。渲染过程就是给出这些因素决定每个像素值的过程。

  首先来看模型。模型通常是由可视化的建模软件制作,看上去是一个“实体”。然而从计算机的角度来说,模型文件只不过是包含渲染它所需要的数据的文件。真实世界中的物体细节是无法穷尽,极其复杂的。所以我们进行简化,把物体简化成多面体,进一步的,简化成每个面是三角形的多面体。(对于曲面和细节较多的地方,我们可以用更多更小的三角面来拟合)。很显然,每个顶点的相对位置是重要的。它决定了模型的形状。由于需要描述位置,自然就有了选择坐标空间的问题。模型描述自身顶点坐标的空间称为模型空间。于是我们可以得出模型文件里一定要包含模型空间下的顶点坐标。

  通常,在一个渲染的场景中,包含了多个渲染的物体。它们根据不同的位置摆放,当然会构成不同的场景。那么为了描述场景里模型之间的相对位置,我们会选择一个独立于模型的坐标空间,称为世界空间。有了世界空间,我们也可以描述观察者的位置和观察的角度。这个观察者,通常我们可以叫它为camera。这样渲染出来的结果可以视作camera所拍得的图像。要决定观察的结果,显然要知道相机的位置和朝向。另外一点需要指出的是,根据成像投影方式的不同,有正交和透视两种相机。正交相机里无法判断看到的物体离相机的远近,不存在近大远小的关系。符合实际人眼观察结果的是透视相机。物体离相机越远,所成的像就越小。

  现在的问题是,给定世界空间中一个点的位置,和相机的位置朝向。我们要确定这个点最后渲染到图像上哪个位置(它或许是不可见的,这时它也应有一个超出范围的坐标)由这个问题牵扯到了shader学习中常遇见的坐标变换。首先我们关心的是点相对于相机的位置。所以自然我们可以以相机为准建立一个坐标空间来描述被观察者的位置。这个空间被称为view space,观察空间。按照OPENGL的传统,我们以相机右方为+x轴,上方为+y轴,观察方向为-z轴。由此可见观察空间是一个右手系。在相机观察的物体中,远处的物体z值更小。现在问题在于,我们怎样把world space中的坐标变换到view space中。显然这是一个仿射变换,由一次线性变换和一次平移构成。3x3的矩阵只能描述线性变换,为统一起见,引入齐次坐标。将线性变换的矩阵扩充为4x4的矩阵,添加一列(或者一行,取决于使用行向量还是列向量)用于刻画平移。现在任意两个三维坐标空间的仿射变换都可以用一个4x4的矩阵来描述。

  得到了顶点在相机空间的坐标,现在需要决定这个点投影在屏幕上的坐标。首先我们先将这个坐标归一化变换到一个左手系的剪裁空间中,使得点的坐标只有三维,并且任何一个分量超出-1到1范围的点都不在可视范围内。(openGL传统,directx则是0到1)

  正交相机的可视范围是坐标满足约束

  -Far <= z <= -Near,

  -Size <= y <= Size,

  -Size*Aspect <= x <= Size*Aspect

  的点。这里Size和Aspect决定了相机所看到的矩形范围,Far和Near决定了深度范围。现在我们需要通过一个变换,把三个分量的边界映射到-1和1上。由于要改变手性,我们让-Far映射到1,-Near映射到-1.

  解方程组

  -k * Far + b = 1,

  -k * Near + b = -1.

  得出系数为 k= -2 / (Far - Near), b = (Near + Far) / (Near - Far)

  于是得出变换矩阵

  

  对于透视相机,它的可视范围是一个四棱台,或者说视锥体。定义相机竖直方向的张角为FOV,水平和竖直方向可视距离之比为Aspect。它的可视约束为(注意z为负数)

  tan(FOV/2)*z*Aspect <= x <= -tan(FOV/2)*z*Aspect

  tan(FOV/2)*z <= y <= -tan(FOV/2)*z

  -Far<= z <= -Near

  这里由于x,y同时还受到z的约束,因此我们需要利用w分量做齐次化,矩阵最后一行的第三个元素应为-1.使得最后的结果中w分量和变换前的z分量互为相反数。

  和上面类似,-Far映射到Far,-Near映射到-Near.

  解方程组

  k*(-Far) + b = Far

  k*(-Near) + b = -Near 得出 k = (Far + Near) / (Near - Far)     b = 2Far*Near / (Near - Far)

  从而得出透视相机的变换矩阵

  

  利用变换矩阵左乘坐标的列向量,然后利用w分量做齐次除法即可得到归一化以后的坐标(x,y,z)对于可见的点,它每一个分量都在-1到1的范围内。对x,y将其加1除以2再分别乘以屏幕的像素宽高即可得到最终渲染的像素坐标。(opengl的屏幕坐标原点在左下角)z值则是这一点的深度值。

  现在我们来总结一下完成这一系列变换所需要知道的量。

  模型空间下的顶点坐标----->>观察空间下的顶点坐标    这一步需要知道观察者和被观察者的相对位置关系,才能给出模型空间到观察空间的变换矩阵。显然,模型和相机在场景中摆好以后,这个矩阵自然也就知道了。

  观察空间的顶点坐标 ------->>剪裁空间  这一步只需要知道相机相关的参数。深度范围Far-Near,正交相机的size,透视相机的FOV,观察的宽高比Aspect。

  剪裁空间----->> 屏幕像素坐标  只需要指定好像素宽高即可。

  

  

  

时间: 2024-10-22 02:49:24

Unity Shader学习笔记(一)坐标变换的相关文章

Unity Shader 学习之旅-初探

Unity Shader 学习之旅-初探 unity shader 图形图像 美丽的梦和美丽的诗一样 都是可遇而不可求的--席慕蓉 最简单的顶点片元着色器 顶点片元着色器基本结构 Unity Shader基本结构:Shader ,Properties,SubShder,Fallback等. 结构 Shader "ShaderName"{  Properties{  //属性,暴露在inspector面板上  }  SubShader{  //针对显卡A的SubShader  Pass{

unity3d shader 学习笔记1

在unity中我们经常会使用shader,但是从来没有深究过,最近在做项目时遇到相关问题,无从下手,决定系统学习一番,在此前提下把我学习的过程做一个记录.学习过程中参考了浅墨和风宇冲两位高人的博客,本文有不对的地方,还望指正. 按照学习技术的习惯,在第一次接触shader时,我们首先会想这两个个问题, 问题一:什么是shader,它能做什么? 问题二:unity中shader使用的语言是什么? 问题一:shader,也就是着色器,它本质就是一段程序,这段程序的作用是把Mesh(网格)以指定的方式

u3d shader学习笔记1

促使我学习SHADER的重要原因是希望深入理解3D渲染的机制,在此基础上可以灵活达到某种效果与性能的平衡,开发出具有良好体验的VR应用. 因为VR应用体验的好坏,直接由游戏的帧率决定,而游戏的帧率则受到渲染一帧画面所花时间的影响,因此如何提升渲染时间,而又可以得到期望的效果,必须要对SHADER或者说渲染机制有深入的理解. 一.在U3D中,shader是什么?shader即“着色器”,我们之所以能够看到屏幕上丰富多彩的3D画面,正是得益于它.U3D中与shader相关的概念以及它们同shader

Unity 3D学习笔记之一 界面介绍

因为学校的课程,本学期对Unity 3D有学习的要求,在博客中记录下自己的Unity学习之路(内容摘录自书本和视频,书本为Unity 4.x从入门到精通) 一.Unity界面介绍 首先进入Unity3D,在菜单栏,File中new project,选择自己的工作路径初始时我们先默认不引入任何的package. 进入Unity的界面中,先对界面进行一下简要的介绍.区域大致依次分为Hierarchy.Scene.Inspector.Project,和上方的菜单栏(Menu bar)和工具栏(Tool

Unity shader学习之逐像素漫反射光照模型

shader如下: Shader "Custom/Diffuse Fragment-Level" { Properties { _Diffuse ("Diffuse", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { Tags { "LightMode"="ForwardBase"

Unity shader学习之半兰伯特光照模型

半兰伯特光照模型,为Valve公司在开发游戏<半条命>时提出的一种技术,用于解决漫反射光无法到达区域无任凭明暗变化,丢失模型细节表现的问题. 其公式如下: Cdiffuse = Clight * mdiffuse * ( dot(n, l) * 0.5 +0.5 ) 通过这样的方式,将dot(n, l)的结果从[-1,1]映射到[0,1]的范围. 转载请注明出处:http://www.cnblogs.com/jietian331/p/7083289.html shader如下: Shader

Unity shader学习之阴影,衰减统一处理

使用unity AutoLight.cginc文件里的内置函数 UNITY_LIGHT_ATTENUATION shader如下: // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Custom/Forward Rendering" { Properties { _MainTex("Main Texture", 2D) = "

Unity shader学习之屏幕后期效果之调整屏幕亮度,饱和度,对比度

Unity的屏幕后期处理效果,使用MonoBehaviour.OnRenderImage来实现. 如代码如下: PostEffectRenderer: 1 using UnityEngine; 2 3 [RequireComponent(typeof(Camera))] 4 public abstract class PostEffectRenderer : GameBehaviour 5 { 6 protected abstract string ShaderName { get; } 7 8

unity初探学习笔记-hello unity

unity3d是目前使用最广泛的3d游戏引擎之一,本系列教程将使用unity制作一款坦克大战游戏,从而带大家体验一下unity的使用. 这一篇教程主要介绍引擎的安装和环境的搭建,最后,我们会在手机上运行起来unity的第一个程序. 首先在unity的官方网站上下载unity的最新引擎,目前最新的版本是5.3.4,下载地址:http://unity3d.com/cn/get-unity/download?ref=personal 下载后一步步的按照提示安装即可,运行起unity,效果如下: 在这里