物体内能与什么有关的平移与移动的( )和( )有关。

OPENGL坐标变换及数学原理(附代码)
核心提示:键盘WSAD键移动镜头,鼠标拖拽改变镜头方向,类似于OGRE中的“OgreBites::CameraStyle::CS_FREELOOK”。1.坐标变换的一个例子,两种思路理解多个变换的叠加现在考虑Scale(1,2,1...OpenGL坐标变换及其数学原理,两种摄像机交互模型(附源程序)
实验平台:win7,VS2010
先上结果截图(文章最后下载程序,解压后直接运行BIN文件夹下的EXE程序):
a.鼠标拖拽旋转物体,类似于OGRE中的“OgreBites::CameraStyle::CS_ORBIT”。
b.键盘WSAD键移动镜头,鼠标拖拽改变镜头方向,类似于OGRE中的“OgreBites::CameraStyle::CS_FREELOOK”。
1.坐标变换的一个例子,两种思路理解多个变换的叠加
现在考虑Scale(1,2,1);Transtale(2,1,0); Rotate(pi/4,(0,0,1)); 这3个变换(下文用S, T,R简写),作用到原先中心位于原点边长为2的立方体上的情况。
坐标系显示说明及变换前的场景如下:
以上变换用OpenGL(经典管线)和GLM实现代码分别如下:
glMatrixMode(GL_MODELVIEW);glPushMatrix();
glScalef(1, 2, 1);
glTranslatef(2, 1, 0);
glRotatef(45, 0, 0, 1);
glutSolidCube(2);
draw_frame(1.5f);glPopMatrix();
glm::mat4 t = glm::scale( glm::vec3(1,2,1) )
* glm::translate( glm::vec3(2,1,0) )
* glm::rotate( 45.0f, glm::vec3(0,0,1) );glMatrixMode(GL_MODELVIEW);glPushMatrix();
glMultMatrixf(&t[0][0]);
glutSolidCube(2);
draw_frame(1.5f);glPopMatrix();
变换后的场景如下图:
现在,可以用两种思路来理解S(1,2,1); T(2,1,0); R(pi/4,(0,0,1)); 这三个变换的叠加。
全局坐标变换,所有的变换在一个全局的固定的坐标系下进行,所有操作均以这个坐标系为参考,注意缩放相对于原点进行,这时的变换顺序和代码顺序正好相反,为R(pi/4,(0,0,1)); T(2,1,0); S(1,2,1); ;
坐标系变换,变换针对坐标系框架进行,所有操作以当前坐标系为参考,所有变换施加后得到一个新坐标系,在这个坐标系中绘制物体,这时的变换顺序和代码相同,为S(1,2,1); T(2,1,0); R(pi/4,(0,0,1)); ;
两种的图示如下:
这里有几点需要说明或者强调一下。思路1全局坐标变换(左图),最后一步S(1,2,1);相对于全局坐标系的原点,而不是物体中心,所以物体的中心发生了变化。思路2物体坐标系变换(右图),第2步T(2,1,0);以物体坐标系为参考,因为物体坐标系的Y轴在上次变换中被拉长了,所以Y轴的1长度也被拉长了,第3步R(pi/4,(0,0,1));也以物体坐标系为参考,因为物体坐标系的Y轴被拉长了,顶点的旋转轨迹在我们看来是个椭圆,如图中青色所示,同样,所旋转的45度在我们看来也不是45度(物体坐标系并不知道这一点,它只根据当前坐标系进行变换,并且总是觉得自己的旋转轨迹是圆,角度是45度)。
这两种思路显然不是巧合,它们的背后有深刻的数学原理,请接着看下一节。
2.坐标变换的数学原理,两种思路背后的数学解释
因为涉及好多数学公式,这里采用一种新的撰文形式,即PPT加讲解的形式,每页PPT截图后面有旁白解释。如果嫌截图不清楚,文章最后给出了PPT的下载链接。
数域一般取实数集或复数集。基是一组线性无关的向量组,而不一定是互相正交(垂直)的。坐标用列向量表示,在OpenGL中点的坐标也用列向量表示。
基的定义,空间中任一向量均能用基线性表示,另一组基中的向量也如此。T为n介方阵。注意这里T是乘在右边。
Y的公式同理直接写出来了。X,Y均为列向量。注意这里T乘在Y的左边,因为Y是列向量嘛,对比前一页PPT,基变换公式中T乘在左边,这种差异是关键,且往下看。目前讲到的基变换与坐标变换均为线性变换(符合f(ax+by)=af(x)+bf(y)的称为线性),线性变换将原点变换为原点,而OpenGL中的变换可以平移,下面讲到,这是仿射变换。
R表示实数集合,|A|表示A的行列式(determinant,有时也表示为det(A))。限制A的行列式不为0是要求A非奇异(notsingular,invertible,可逆),因为A不可逆时可能将直线映射为一点,即将n维空间压缩为小于n维。另外A的行列式如果为负则变换产生镜像(如将右手系变换为左手系)。之前用X,Y表示坐标也即列向量,现在用粗体小写字母x,y,b表示列向量。PPT中用到了分块矩阵表示,注意A为n×n,x,y,b为n×1,粗体0是1×n个0。xT表矩阵转置(transposition)。扩充第n+1个坐标是为了能够表示平移,也就是说n+1维空间的线性变换可以表示n维空间的平移。第n+1个坐标还可以用来分辨n维空间中的点与向量(或者说是方向),即第n+1个坐标不为0时表示点,为0时表示向量,不为0且不为1时要将所有坐标都缩放一个倍数使之为1。第n+1个坐标为0时可以从两个角度理解,一是理解成两个点的差,点的第n+1个坐标都是1,做差后第n+1个坐标为0,两个点的差也就是向量,二是将其理解为第n+1个坐标w是从1逼近0,这时可以表示无穷远处的点,也就是一个方向。注意这里的变换矩阵T有固定的形式,即最后一行为n个0接1个1,仿射变换只是n+1维空间的特殊线性变换(自由度小于(n+1)2小于等于n2+n)。如果T的最后一行的前n个元素不为0,那么变换可能将直线变为曲线(请自行举例),即变换后的坐标是原坐标的有理分式(这在OpenGL投影矩阵中被应用)。
原基为向量,前n个元素是齐次坐标系中的向量,这里将线性变换(没有平移)推广到仿射变换,即加入原点,原点是新基中唯一的点(其他为向量)。
这里将之前用的字母T改用A,现在的T表示齐次坐标下的变换矩阵(见PPT第2页)。注意b其实是第一组基下的坐标(和A一样),这组坐标和基相乘得到它表示的向量。这里再次注意T在基变换和坐标变换公式中的位置。强调一下,T并不是自由的n+1介方阵,它的最后一行固定为n个0接1个1。第n+1个坐标w为0时表示的向量(认为是两个点的差),向量的仿射变换可以看成是其两个端点仿射变换后做差,这时T的平移部分将被抵消(请看下一节仿射变换的分解),也就是说w为0的向量的仿射变换只和T的旋转和缩放部分有关(也就是自由向量的概念,向量只有方向和大小,没有起点)。
这里顺便提一下,矩阵相乘的几何意义就是变换的叠加,即线性映射的叠加。注意一个细节,这里的每个T既表示仿射变换本身,又表示仿射变换的变换矩阵,并没有加以区分,这是合理的,因为仿射变换和仿射变换矩阵之间有一一对应的关系(所有仿射变换构成的空间和所有仿射变换矩阵构成的空间同构)。至此彻底了解了两种思路的数学原理。再次强调这里的仿射变换T可能不一定是刚体变换,它有可能产生缩放、错切变形。第1节的例子就不是刚体变换,以上的两种思路和解释是对仿射变换成立的,不限于刚体变换(旋转和平移或其叠加)。
3.更深入的数学,坐标变换的分解(矩阵的分解)
接着用PPT的形式~
这里都讲的是三维空间。I表示单位矩阵(identitymatrix,数学书中一般用E表示)。||v||表示范数,在向量空间中也就是向量的模长。注意到 (u·x)u=(uuT)x,u×x(叉乘)等于 u波浪线矩阵乘 x,旋转公式只要选定 u,u×x, x-(u·x)u 三个新基就很好看懂了(文献第11页)。这里既用字母T表示仿射变换矩阵,又用其表示平移矩阵,T的具体含义可以根据上下文区分不会混淆,用C++术语来说,它们的参数列表不同。旋转矩阵沿xyz轴的特殊形式请自行将v设为特殊值进行推导。那现在的问题是,任意给一个仿射变换矩阵T(要符合最后一行是n个0接1个1),T能否分解为T(x,y,z),S(x,y,z), R(a,(x,y,z))的组合(连乘积)呢?答案是肯定的,请继续往下看。
再次,既用T, R,S表示矩阵,又用其表示平移、旋转、缩放函数,请很据上下文区分。行列式为正的正交矩阵是一个旋转矩阵,对称矩阵是个缩放矩阵(缩放值可能有负值,这时产生镜像,即手性变化),经过对角化后分解为旋转矩阵和沿xyz轴缩放的矩阵(即对角阵)。注意极式分解具有唯一性,对角化不具有唯一性,但不唯一性也仅限于调换对角阵的行或列(相应调换对角阵两边的旋转矩阵)。可以根据T,S,R(平移、缩放、旋转)的逆来构造整个变换T的逆((AB)-1=B-1A-1,当然也可以不分解直接求逆矩阵)。
4.图形学中的变换模型以及OpenGL的实现
这里讲的变换模型是指一种“思维模型”,也就是说用这个模型去思考可以很方便对物体位置和定向进行操作,而具体的实现能够保证按照这个模型思考一定能够得到正确答案,但这个实现可能根本就不是按部就班的按照模型实现具体坐标的计算,所以还要讲OpenGL的实现。
图形学中的变换模型一般涉及物体坐标系(model space)、世界坐标系(world space)、视觉坐标系(eyespace)、规范化设备坐标系(normalized device space)、窗口像素坐标系(windowspace),这些坐标系中的坐标相应叫做某某坐标,如世界坐标系中的坐标叫做世界坐标(worldcoordinates)。一个示意图如下(用Blender软件制作和渲染的):
如图中所标注的,猴头上面的坐标框架表示物体坐标系;那个最大的坐标框架是世界坐标系,水红色的是地板;黑色的摄像机上的是视觉坐标系,视觉坐标系的定义是,镜头所指方向为z负方向,摄像机正上为y正方向,右手法则确定x方向。
坐标的变换如下(请见文献第66页):
1.模型变换,视图变换
现在举例子说明物体坐标到视觉坐标的变换,场景是(请看上面猴头那个图),猴头的中心位于物体坐标系原点,猴头中心位于世界坐标系的(1,1,1)处,摄像机位于世界坐标系的(0,1,5),摄像机的向上方向沿世界坐标系y正方向,摄像机镜头对准世界坐标系z负方向。对猴头中心来说,它在物体坐标系中坐标(0,0,0),世界坐标系中坐标(1,1,1),视觉坐标系中坐标(1,0,-4)。模型变换矩阵为T(1,1,1),视图变换矩阵为T(0,-1,-5),如果把模型和视图矩阵合起来就是T(0,-1,-5)T(1,1,1)=T(1,0,-4)(还记得,T(x,y,z)表示平移)。GLM和OpenGL函数中的LookAt函数返回的变换矩阵是,将摄像机设置为函数参数指定的情况所需要的视图矩阵。
2.投影变换
投影变换请见下图(摘自文献):
投影变换后进入坐标裁剪,即落在红色方框外的部分将被裁剪掉。
3.透视除法
投影变换后齐次坐标的第4个分量w可能不为1,透视除法即将xyz分量都除以w,得到规范化设备坐标(特点是xyz分量范围在-1到+1之间),对透视投影而言,这一步是非线性的(远处物体被压缩)。如下图(摘自文献):
投影变换和透视除法合起来的效果是,将指定的视景体(也叫平截头体,也就是那个裁剪框)变换为边平行于xyz轴且xyz范围都是-1到+1中心位于原点的正方体。注意z坐标的符号变化。如下图(摘自文献):
4.视口变换
再经过视口变换,即调用OpenGL的glViewport函数,对应到窗口像素,注意,在OpenGL中,像素坐标系的原点位于左下角,向右为x轴正向上为y轴正(而一般图片像素都是以左上角为原点)。具体来说,视口变换将规范化设备坐标的位于[-1,1]之间的z坐标对应到深度值,一般在[0,1](值越小离摄像机越近,z=-1对应d=0,z=+1对应d=1,d为深度值),将(-1,-1,z)对应到屏幕(0,0,d)点,其中d为深度值,将(1,1,z)对应到屏幕(w,h,d)点,其中w,h为窗口的宽和高,其他点按线性插值。如下图(摘自文献):
5.OpenGL实现
具体到OpenGL的实现,OpenGL和数学中相同采用右手系,OpenGL把模型变换和视图变换合二为一,即模型视图矩阵。OpenGL和GLM的变换矩阵都是按照列优先存储在内存中,这和C++二维数组不同,其实,GLM中的4x4矩阵是由4个列向量组成的。按照上面的分析,当OpenGL的模型视图矩阵和投影矩阵均为单位阵时,这时摄像机位于世界坐标系原点看向z负方向,向右方向沿x轴正方向,向上方向沿y正方向,由于投影矩阵为单位阵,这时为正交投影(另一种是透视投影),裁剪面为xyz的±1,也就是说,对应到最后的显示窗口,x方向向右,y方向向上,z方向垂直屏幕向外,窗口中心对应坐标原点,窗口边缘对应±1,并且z值小的片断遮挡z值大的片断(正好和离摄像机的远近关系反了,这是因为没有对z坐标进行变号)。对了,OpenGL除了模型视图矩阵和投影矩阵之外,还有文理坐标变换矩阵和颜色变换矩阵。请见OpenGL官方手册文献2.12和2.16。
5.两种摄像机交互模型
现在用前面的知识实现两种最常见的摄像机交互模型,先说下对上面说的变换模型的实现,程序有如下全局变量:
glm::mat4 transform_camera(1.0f); // 摄像机的位置和定向,即摄像机在世界坐标系中位置glm::mat4 transform_model(1.0f);
// 模型变换矩阵,即物体坐标到世界坐标glm::vec4 position_light0(0);
// 光源位置,世界坐标系中的坐标float speed_scale=0.1f;
// 鼠标交互,移动速度缩放值
在绘制函数中,这些全局变量被应用如下(第一行之所以求逆,是因为model_view_matrix表示的是视觉坐标到世界坐标的变换矩阵,也就是摄像机在世界坐标系中的位置,这里需要的是将世界坐标变换到视觉坐标):
glm::mat4 model_view_matrix = glm::affineInverse(transform_camera);glMatrixMode(GL_MODELVIEW);glLoadMatrixf(&model_view_matrix[0][0]);glLightfv(GL_LIGHT0, GL_POSITION, &position_light0[0]); // 位置式光源draw_world(10,3, true, true, true); // 绘制世界model_view_matrix *= transform_glMatrixMode(GL_MODELVIEW);glLoadMatrixf(&model_view_matrix[0][0]);draw(); // 绘制物体
第一种拖拽球模型(姑且叫做拖拽球吧),它设想窗口中心有个虚拟的球,假设鼠标位于这个球面靠近我们的半面,即z≥0的半球面(使用MeshLab软件制作):
数学描述如下,图中的坐标系是视觉坐标系,对应到屏幕也就是向右为x轴,向上为y轴,垂直屏幕向外为z轴:
推导旋转矩阵如下:
这里都假设 a, b 两点在距离屏幕中心小于r的情况,如果大于r(其实是大于水红色圆半径)请见上图的中的 p 点,用 p撇 代替 p 点,水红色圆半径小于r是希望鼠标在距离中心大于r的地方沿径向移动物体也能产生旋转。OpenGL官方Wiki上有个更好的解决方法,见文献。
第二种漫游模型(姑且叫漫游吧),要求按下键盘WSAD键摄像机前进、后退、左移、右移,鼠标左右移动时摄像机镜头左右扫动,鼠标上下移动时摄像机镜头做俯仰动,如下图所示:
注意鼠标左右移动的旋转轴要沿世界坐标系的y轴旋转,而不是摄像机自己的y轴,以防止视角倾斜,鼠标上下移动就沿摄像机自己的x轴旋转就行。键盘WSAD键沿摄像机的z轴和x轴移动就行。
下面是程序实现,程序中的键盘和鼠标响应如下:
1.WS键,摄像机沿视觉坐标系z轴移动,AD键,摄像机沿视觉坐标系x轴移动;
transform_camera *= glm::translate( speed_scale*glm::vec3(dx,0,dz) );
2.上下键,摄像机沿世界坐标系y轴移动,这里v为世界坐标系的y轴单位向量(不是点,所以第四个分量为0,这点很重要,若写成vec4(0,1,0,1)将得到错误结果)在视觉坐标系中的坐标;
glm::vec3 v = glm::vec3( glm::affineInverse(transform_camera)*glm::vec4(0,1,0,0) );transform_camera *= glm::translate( speed_scale * dy * v );
3.左右键,摄像机沿视觉坐标系z轴旋转;
transform_camera *= glm::rotate( speed_scale*dx, glm::vec3(0,0,1) );
4.鼠标右键拖拽,上下移动时摄像机沿视觉坐标x轴旋转,左右移动时摄像机沿世界坐标系y轴转动;
transform_camera *= glm::rotate( speed_scale*dy, glm::vec3(1,0,0) );glm::vec3 v = glm::vec3( glm::affineInverse(transform_camera)*glm::vec4(0,1,0,0) );transform_camera *= glm::rotate( -speed_scale*dx, v );
5.鼠标左键拖拽,物体按拖拽球旋转;
void drag_ball(int x1, int y1, int x2, int y2, glm::mat4& Tmodel, glm::mat4& Tcamera){
float r = (float)std::min(win_h, win_w)/3;
float r2 = r*0.9f;
float ax = x1-(float)win_w/2, ay = y1-(float)win_h/2;
float bx = x2-(float)win_w/2, by = y2-(float)win_h/2;
float da = std::sqrt(ax*ax+ay*ay), db = std::sqrt(bx*bx+by*by);
if(std::max(da,db)&r2){
if(da&db){ dx = (r2/da-1)* dy = (r2/da-1)*
dx = (r2/db-1)* dy = (r2/db-1)* }
ax += ay += bx += by +=
float az = std::sqrt( r*r-(ax*ax+ay*ay) );
float bz = std::sqrt( r*r-(bx*bx+by*by) );
glm::vec3 a = glm::vec3(ax,ay,az), b = glm::vec3(bx,by,bz);
float theta = std::acos(glm::dot(a,b)/(r*r));
glm::vec3 v2 = glm::cross(a,b);
// v2是视觉坐标系中的向量,v是v2在物体坐标系中的坐标
glm::vec3 v = glm::vec3(
glm::affineInverse(Tmodel) * Tcamera * glm::vec4(v2[0],v2[1],v2[2],0) );
Tmodel *= glm::rotate( theta*180/3.14f, v );}
6.鼠标中键拖拽,相当于AD键和上下键;
7.鼠标中键滚动,相当于WS键。
以上代码,以可读性和方便说明原理为目标,所以实现上不很高效,尤其是用transform_camera表示摄像机位置和定向而不是视图矩阵,导致每次都要求transform_camera的逆,可以利用(AB)-1=B-1A-1等公式进行等价变换提高效率。
6.进阶,变换的插值
很多时候,我们希望对变换进行插值,比如,指定物体在开始和结束两个时刻的位置和定向(即物体的transformation),希望在这两个时间点的中间时刻物体能够平滑的变换,从而实现关键帧动画,再比如,我们指定开始和结束两个时刻的摄像机的transformation,希望摄像机的transformation能够被插值,从而实现视角的平滑变化。这个问题可以归结为T(0)=Tbegin,T(1)=Tend,求T(t),0begin+(1-t)Tend这个函数并不能做到定向(旋转)的平滑变化,甚至都做不到保持物体形状不变(刚体变换)。解决方法涉及高深的数学知识,如矩阵的指数和对数,甚至是群论和李代数,请参考文献。
源程序下载:链接/s/1hqrG98K 密码: jmc5
PPT下载(如果下载后显示要修复,请右键文件,属性,点下面解除锁定按钮):链接/s/1c0lJigw 密码: isds
Ochiai, H. and Anjyo, K., Mathematical basics of motion anddeformation in computer graphics. in ACM SIGGRAPH 2014 Courses,(Vancouver, Canada, 2014), ACM, 1-47.(到ACM网站下载,可能需要大学IP);
《高等代数简明教程》(上册,第二版,蓝以中编著,北京大学出版社,2007),第4章(到当当网买);
Angle E, Shreiner D. Interactive Computer Graphics: A Top—downApproach with Shader-based OpenGL. 2011. Chapter 3(到亚马逊买);
/,Tutorial 3.
OpenGL Mathematics(GLM), Open source library.
《OpenGL编程指南》(原书第7版,Dave Shreiner等著,李军等译,机械工业出版社,2011),第3章(到当当网买);
University of Freiburg的ComputerGraphics小组课程主页(去课程主页,图形管线PPT);
OpenGL 3.3 Compatibility ProfileSpecification (updated March 11, 2010), 2.12, 2.16(去官网下载);
https:///wiki/Trackball。u3d中平移搬操作是移动摄像机实现还是移动物体?_unity3d吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:52,295贴子:
u3d中平移搬操作是移动摄像机实现还是移动物体?收藏
我有平移物体的代码,但是移动整个场景是不是更卡?下图的平移代码求改成移动摄像机
和张大佛爷、二月红一起去探秘矿洞墓穴!
using UnityEusing System.Cpublic class Translation : MonoBehaviour {
//平移同步对象
private Vector3 screenP
private Vector3
void Start (){
void Update (){
if(Input.GetMouseButtonDown(2))
screenPoint = Camera.main.WorldToScreenPoint(target.transform.position);
offset = target.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
if(Input.GetMouseButton(2))
Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) +
target.transform.position = curP
移动物体,相机随着物体移动,
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或知识点梳理
电路的动态分析1、知识点的认识此类问题能够综合考查串联的电流、电压、电阻关系、、电功、等知识的掌握和综合运用情况.2、解题方法点拨此类问题解题之前要弄清的问题是:①看懂电路图,弄清各电表测量哪些用电器的哪些物理量;②弄清改变电阻的方法,利用滑动变阻器改变电阻还是利用开关改变电阻;解题方法有两种:方法①:按这样的顺序分析:局部电阻如何变化→总电阻如何变化→由于电源电压不变,导致电路中电流如何变化→依据U=IR分析不变的电阻两端的电压如何变化→依据U变化的电阻=U总-U不变的电阻分析变化的电阻两端的电压如何变化.方法②:串联电路中U1R1=U2R2,即电压与电阻成正比,由此可知串联电路中按电阻的比例分配电压,电阻所点比例分数越大,分得的电压比例就越大.
串联电路电压规律:在串联电路中,总电压等于各用电器两端的电压之和,即U=U1+U2+…+Un。
整理教师:&&
举一反三(巩固练习,成绩显著提升,去)
根据问他()知识点分析,
试题“如图所示.物体M在水平导轨上平移时,带动滑动变阻器的滑片P移...”,相似的试题还有:
如图所示电路,R1>R2,当闭合S1断开S2,滑动变阻器的滑片P放在变阻器的中点时,电压表的示数为U0.关于此电路的下列说法中,正确的是()
A.闭合S1断开S2时,若滑动变阻器的滑片P向左移动,电压表的示数将大于U0
B.若断开S1闭合S2,同时滑动变阻器的滑片P向右移动,电压表的示数可能等于U0
C.若同时闭合S1、S2,无论滑动变阻器的滑片怎样移动,电压表的示数总等于U0
D.断开S1闭合S2,若使电压表的示数还等于U0,则滑动变阻器的滑片P应向左移动
计算机鼠标内装有起自动控制作用的传感器,如图是它的工作原理示意图,当物体M在导轨上平行移动时,可带动与之相连的金属划片P移动,通过电压表示数可反映物体M移动的距离,当物体M向右移动时()
A.电流表示数将变大,电压表示数将变大
B.电流表示数将不变,电压表示数将变大
C.电流表示数将变大,电压表示数将变小
D.电流表示数将不变,电压表示数将变小
小丽设计了如图所示的简易电子距离测量仪,R是一根粗细均匀的电阻丝,其每厘米长的电阻为0.5Ω,电路各部分均接触良好.物体M只能在导轨上做直线运动,并带动与之相连的金属滑片P移动,电压表示数可反映物体M移动的距离.开始测量前,将金属滑片P置于电阻丝中点,此时电压表和电流表示数分别为1.5V和0.2A.由此可知()
A.电阻丝的总电阻为7.5Ω
B.当电压表示数为2V时,物体M向右移动了5cm
C.当电压表示数为1V时,物体M向左移动了5cm
D.若开始测量前,将金属滑片P置于电阻丝某端点,可测量的最大距离30cm.C# 类中base和this的有关问题,求帮忙_struts2 mvc视图范例分析(转)_Unity3D游戏制作(三)——移动平台下的角色阴影制作__脚本百事通
稍等,加载中……
^_^请注意,有可能下面的2篇文章才是您想要的内容:
C# 类中base和this的有关问题,求帮忙
struts2 mvc视图范例分析(转)
Unity3D游戏制作(三)——移动平台下的角色阴影制作
C# 类中base和this的有关问题,求帮忙
C# 类中base和this的问题,求帮忙using S
using System.Collections.G
using System.L
using System.T
namespace CLR_TEST1
public class BaseClass
public virtual void Print()
Console.WriteLine(this.GetType().FullName);
Console.WriteLine("BaseClass Print");
public class DirClass : BaseClass
public override void Print()
base.Print();
Console.WriteLine(base.GetType().FullName);
Console.WriteLine("DirClass Print");
class Program
static void Main(string[] args)
DirClass b = new DirClass();
b.Print();
Console.Read();
我期待的结果是:
CLR_TEST1.BaseClass
BaseClass Print
CLR_TEST1.BaseClass
DirClass Print
可结果是:
CLR_TEST1.DirClass
BaseClass Print
CLR_TEST1.DirClass
DirClass Print
这是为什么?
------解决方案--------------------
Quote: 引用:
如果非要说出道理,就是你必须理解,人是动物的含义就是人是一种具体的动物,而不是人的肚子中还装着一个动物。尽管可能语言/框架库的实现可能具有这样的堆积,但是从概念上来说,不存在在当前类型中还有一个基类的类型。继承后只有一个类型。
你好,是不是可以这样理解,GetType的方法是属于Object类的,也就是说GetType是在Object类型对象的方法表里面,又由于DirClass的实例在托管堆上对应的内存,其实是包括基类中的一些内容(比如基类的实例字段),也就是说无论在DirClass中Print方法用的是this,或者 base,指的都是当前DirClass的实例,即运行实例,我可能讲不清楚,我只能这样理解了,不知对不对
我觉得你说得挺对的。MSDN上说:Gets the Type of the current instance.关键是the current instance.------解决方案--------------------对于常用的控件,基本都有五六层的继承,但无论有多少层,this 作为实例肯定只有一个。
base 其实与类无关,只是方便调用基类的方法的一种语法,以前 C++ 时要用【基类类名::方法名】调用的。------解决方案--------------------
Quote: 引用:
Quote: 引用:
Quote: 引用:
如果非要说出道理,就是你必须理解,人是动物的含义就是人是一种具体的动物,而不是人的肚子中还装着一个动物。尽管可能语言/框架库的实现可能具有这样的堆积,但是从概念上来说,不存在在当前类型中还有一个基类的类型。继承后只有一个类型。
你好,是不是可以这样理解,GetType的方法是属于Object类的,也就是说GetType是在Object类型对象的方法表里面,又由于DirClass的实例在托管堆上对应的内存,其实是包括基类中的一些内容(比如基类的实例字段),也就是说无论在DirClass中Print方法用的是this,或者 base,指的都是当前DirClass的实例,即运行实例,我可能讲不清楚,我只能这样理解了,不知对不对
我觉得你说得挺对的。MSDN上说:Gets the Type of the current instance.关键是the current instance.
是不是可以这样理解,无论你实例哪一个引用类型,即使这个引用类型有N层继续关系,子类实例时会调用父类的构造函数,这个调用父类的构造函数过程并不是指在堆上单独的去创建了父类的实例,而是说,实例一个子类,堆上只会分配创建子类需要的一些内存,调用父类的构造函数只不过是在已分配给子类对象内存中去初始父类的实例字段,子类的实例对象方法内部用base其实是在子类对象内存块中去引用父类的一些特性,但其实对于运行实例来讲就只有子类对象一个,对于GetType是子类要一直回溯到System.Object类型对象中方法表中才能找到的方法,这个方法的返回的是当前堆上调用他的对象的TYPE,虽然base指的是引用子类对象内部父类的特性,但他还是属于当前子类对象的?
在你讨论问题的时候,一定要分清概念和实现。从实现上说,肯定一个继承的类型包含了基类的所有信息的存储。但是无论什么OOP语言,都不会出现继承的类中还套着一个基类这样的概念。------解决方案--------------------public class BaseClass
protected object o = 1;
public virtual void Print()
Console.WriteLine(o.GetType());
Console.WriteLine(this.GetType().FullName);
Console.WriteLine("BaseClass Print");
public class DirClass : BaseClass
new object o = "2";
public override void Print()
base.Print();
Console.WriteLine(o.GetType());
Console.WriteLine(base.o.GetType());//注意这里~!可以指向父类的成员,但为什么base.GetType()是指向当前,我也不知道了~
Console.WriteLine("DirClass Print");
public virtual void Print()
Console.WriteLine(this.GetType().FullName);
Console.WriteLine("BaseClass Print");
你这个是方法列表,没有用到成员变量
无论实例化多少,方法列表只有一份~而且this只指向当前的调用者------解决方案--------------------
Quote: 引用:
public class BaseClass
protected object o = 1;
public virtual void Print()
Console.WriteLine(o.GetType());
Console.WriteLine(this.GetType().FullName);
Console.WriteLine("BaseClass Print");
public class DirClass : BaseClass
new object o = "2";
public override void Print()
base.Print();
Console.WriteLine(o.GetType());
Console.WriteLine(base.o.GetType());//注意这里~!可以指向父类的成员,但为什么base.GetType()是指向当前,我也不知道了~
Console.WriteLine("DirClass Print");
public virtual void Print()
Console.WriteLine(this.GetType().FullName);
Console.WriteLine("BaseClass Print");
你这个是方法列表,没有用到成员变量
无论实例化多少,方法列表只有一份~而且this只指向当前的调用者
谢谢你的回复,this.GetType()这个比较好理解
但base.GetType()返回的还是子类的类型,这点难理解,111楼版主大大讲的太深奥了
我也不明白,呵呵,我在网上好像都搜不到这样类似的问题
难道是这问题太EASY了?想知道大家对这个问题的理解
如果你学过编译原理,如果你了解c++对象模型,你就不会奇怪了。
gettype不是虚函数,你的类并没有把他“替换”,所以他只能去掉用object的gettype,而且可以推测object的gettype会根据传入this指针确定如果输出
其实在最新版的《编译原理》那本书提到了.net面向对象部分的实现传承c++的方式
struts2 mvc视图范例分析(转)
struts2 mvc视图实例分析(转)
从一个高度层次来看,Strtsu2属性pull-MVC(或是MVC2)框架;这与传统的MVC框架略有不同,因为action的角色采用模型,而不是控制器,虽然这样有一些重叠."pull"来自于视图从action拉数据,而不是持有一个分开的可用模型对象.我们知道在概念方面意味着什么,但在执行层面意味着什么?这个模型-视图-控制器模式在Struts2中被实现成了五个核心部分--actions,拦截器,值栈/OGNL,结果类型和结果/视图技术.图1:The MVC / Struts2 Architecture图1描述了从Struts2高度层面上的结构, 包含了模型,视图和控制器.控制器是一个实现了Struts2里分派Servlet过滤器以及拦截器,模型实现的actions,视图则是由结果类型和结果的组成.值栈和ONGL提供了一个通用的路线,链接和集成其他组件.我们在这章讨论一些通用的部分,将会有很多信息涉及到配置.配置WEB应用,配置actions,拦截器,结果等等.记住,这些说明只是为了提供如果完成的背景,或许不是最有效的配置应用的方式.在后面的章节我们将讨论更加容易和更有效的方式以完成同样的目标,使用约定俗成的配置,注释和零配置插件.在我们进入详细讲解核心构成之前我们先来讨论一下全局的配置.Configuration在配置Struts2之前,你需要先下载发行版或是在你的Maven2的"pom.xml"文件中配置它的依赖关系:&dependency&
&groupId&org.apache.struts&/groupId&
&artifactId&struts2-core&/artifactId&
&version&2.0.6&/version&&/dependency&做完之后,Struts2应用的配置就可以分成三个独立的文件,如图2中所示.图 2: Configuration file scope for framework elements需要在你的"web.xml"文件里为WEB应用的配置FilterDispatcher Servlet过滤器: 1 &filter&
&filter-name&action2&/filter-name&
&filter-class&
org.apache.struts2.dispatcher.FilterDispatcher
&/filter-class&
6 &/filter&
7 &filter-mapping&
&filter-name&action2&/filter-name&
&url-pattern&/*&/url-pattern&
10 &/filter-mapping&
它配置了基本的WEB应用.剩下来的事就是使用"struts.properties"配置文件来自定义WEB应用的执行环境,通过"struts.xml"配置文件来配置WEB应用的构件.我们现在来看看这两个配置文件的详细描述.The struts.properties File这个配置文件提供了一个改变框架默认行为的机制.一般情况下,你是不需要修改这个文件的,一个例外是你想配置一个对于开发者友好的调试环境.所有的包含在"struts.properties"配置文件中的配置项,都可以使用"init-param"标签配置在"web.xml"文件中,同样也可以使用"constant"标签配置在"struts.xml"配置文件中.Properties允许修改如改变Freemarker选项,替换action的映射类,决定是否重新加载XML配置文件,默认使用的用户界面主题等等.更多的关于properties的最新信息,请查阅位于http://struts.apache.org/2.x/docs/strutsproperties.html的Struts2的Wiki.一个默认的名为"default.properties"配置文件包含在发行版的Struts2-Core JAR文件中.想要修改一个配置项,只需在你项目的源文件的类路径的根目录里创建一个名为"struts.properties"的文件.之后,添加你想修改的配置项.新的值将覆盖掉默认值.在开发环境中,这有一些配置项你可能需要更改:struts.i18n.reload = true -- 能够重新加载国际化文件 struts.devMode = true -- 能够在开发模式下提供更全面的调试 struts.configuration.xml.reload = true 能够重新加载XML配置文件(适用于action),修改之后可以在Servlet容器里重新加载整个WEB应用 struts.url.http.prot = 8080 -- 设置服务运行的端口(以便生成正确的URL) The struts.xml File"struts.xml"文件包含配置信息,你将修改它用于发布action,我们将在本章剩余的部分详细的讲解指定的元素.现在,让我们看看不会改变的结构.注:根据你应用的功能,可能会从你的应用中移除"struts.xml"文件.配置上我们将在本章讨论一种替换的方法,使用注释,"web.xml"启动参数,和交替的URL映射配置.唯一的配置仍然需要"struts.xml"文件,如全局结果,异常处理,和自定义的拦截器栈.这是一个XML文件,所以第一个元素是XML的版本和编码信息.下一个则是XML的文档类型定义(DTD).DTD提供了一些文件里的元素的结构,并最终用XML来解析和编辑. 1 &?xml version="1.0" encoding="UTF-8" ?&
2 &!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration
"http://struts.apache.org/dtds/struts-2.0.dtd"&
6 &struts&
name="struts2"
extends="struts-default"
namespace="/struts2"&
&/package&
13 &/struts&
我们现在到达了&struts&标签.这是Struts2具体配置的最外层标签.所有其他的标签都将被包含在这个当中.The Include Tag:&include ... /&标签用于将Struts2应用模块化时引入其他配置文件时使用并且它始终是&struts&标签的子标签.它仅仅包含一个"file"属性,用于提供被引入的文件的名称,那个文件必须与"struts.xml"配置文件的结构相同.例如,如果你想拆分一个计费应用的配置文件,你可以选择按组将缴费,管理和报表配置到单独的文件中:
1 &struts&
&include file="billing-config.xml" /&
&include file="admin-config.xml" /&
&include file="reports-config.xml" /&
6 &/struts&
引入文件时,顺序是重要的.来自引入文件的信息将在文件中引入标签的位置开始有效.因此,使用一个配置在另一个文件中的标签,这个引入的配置必须出现在被引用之前.有一些文件你是必须明确的引入,还有一些文件是自动引入的.比如说"struts-default"和"struts-plugin.xml".两者都包含了默认的结果类型,拦截器,拦截器栈,包,以及WEB应用执行环境的配置信息(也可以配置在"struts.properties"文件中)的配置.区别在于"struts-default.xml"文件提供的是Struts2的核心配置,而"struts-plugin.xml"提供的是详细插件的配置.每个插件JAR文件中都应包含一个"struts-plugin.xml"文件,所有这些都将在启动的时候加载.
Unity3D游戏制作(三)——移动平台下的角色阴影制作
Unity3D游戏制作(三)——移动平台上的角色阴影制作本文将重点介绍两种目前在移动平台上的主流阴影制作技术,同时也会简单介绍两种移动平台上相对较为高级的动态阴影生成方法。
由于目前主流使用Unity3.x在移动平台上并不支持阴影的动态生成技术,所以目前最普遍流行同时性价比也最高的阴影生成方法有以下两种:
简单贴图法
所谓简单贴图法即是直接在角色的角底附加一个阴影半透明贴图,并让其跟随角色一起运动,一般是将该阴影Object成为角色模型的子物体,该种阴影生成效果如下:
阴影的Inspector视图如下:
其中ShadowComponent是控制阴影的一个脚本,与其生成无关,故不再这里介绍。影响该应用生成的主要有两个部分,一个是Shadow网格,另外一个则是渲染所需要的材质“No Name”。需要注意的是,Shadow网格是一个平面,但不建议使用Unity自身生成的Plane,因为Unity生成的面数较多,可通过3DMax等建模工具来自行建模,如下所示:
对于材质,最重要的则是Shader的书写,我将其列在下方,以方便大家使用:
Shader "iPhone/SimpleShadow"
Properties
_MainTex ("MainTex", 2D) = "" {}
Tags { "Queue" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Color [_clrBase]
Lighting Off
SetTexture [_MainTex] { combine texture, one - texture }
通过以上设置即可生成最简单的阴影效果,大家可以通过自己设定脚本来控制阴影的移动和变化等等。
但是,该阴影生成方法有一个明显的“硬伤”,即该阴影只能适用于平坦的地面,一旦地面凹凸不平或有遮挡物,则会出现“穿帮”的效果,如下图所示,该方法生成的阴影对脚下的正方体完全没有影响,所以为了解决这种问题,投影生成法应运而生。
投影生成法
该方法本质上来说是一种贴花(Decal)技术,即设定一个投影器,然后将阴影贴图投射到你想展现阴影的地方,该方法的优点在于投影效果不取决于被投影区域的几何形状,即被投影区域可以任意凹凸的曲面,也可以处理各种障碍物。该方法生成的阴影效果如下:
下面我就具体介绍一下该阴影的生成方法:
通过“GameObject-&Create Empty”来创建一个空的物体,并取名为“Shadow Projector”。
通过“Component-&Effects-&Projector”在该空物体上加入Projector组件,并通过平移、旋转和调整参数达到如下效果:
然后在Material选项中拖入已经准备好的材质,即可投影出阴影,效果如下:
我们看到,场景中不仅生成了阴影,同时角色的身体也“变黑”了,这是因为投影器的“Ignore Layers”设定为“Nothing”的缘故,我们将可忽略层设为角色的层“Player”,则可使角色不再被投影,效果如下:
最终的Shadow Projector的Inspector视图如下:
其中材质所用到的shader为:
Shader "Projector/Multiply" {
Properties {
_ShadowTex ("Cookie", 2D) = "gray" { TexGen ObjectLinear }
_FalloffTex ("FallOff", 2D) = "white" { TexGen ObjectLinear
Subshader {
Tags { "RenderType"="Transparent-1" }
ZWrite Off
Fog { Color (1, 1, 1) }
AlphaTest Greater 0
ColorMask RGB
Blend DstColor Zero
Offset -1, -1
SetTexture [_ShadowTex] {
combine texture, ONE - texture
Matrix [_Projector]
SetTexture [_FalloffTex] {
constantColor (1,1,1,0)
combine previous lerp (texture) constant
Matrix [_ProjectorClip]
通过以上的步骤,我们即可实现投影式的阴影生成方法。与第一种方法一样,可以设定一些特定脚本来控制该阴影的移动以及变化等等。另外,需要注意的一点是“Ignore Layers”选项的设定,原则上是尽量去掉那些不需要被投影的层,从而来减少不必要的计算量。
上述两种方法是目前移动平台上的主流阴影生成方法,优点是生成简单,使用方便、计算量较小,但缺点同样突出,即该阴影是假的,并不是真的物体投射阴影,所以真实感并不强。下面我就介绍两种目前可以在移动平台上使用的实时动态阴影生成方法,不过在这篇中我们只介绍阴影效果,并不介绍具体的实现技术和生成方法,留待以后高级教程中讲解。
3、 Shadow Map方法
虽然Unity目前并不支持在移动设备上生成动态阴影,但同样可以通过RenderTexture来生成简单的Shadow Map,效果图如下所示:
我们看到所生成的阴影存在锯齿,这是由于Shadow Map分辨率不够所致,你可以通过增加RenderTexture的分辨率来减少锯齿的影响,如下图所示,当然这样做的同时也会带来一定的渲染消耗。
环境遮挡方法
该方法取自于游戏《Shadow Gun》,通过分析物体的近似环境遮挡信息来实时生成动态阴影,效果如下图所示:
其生成方法大致为如下三步:
首先在角色脚底生成一个显示阴影的网格。
其次,根据分别在角色的跨步以及两腿处生成三个圆球,通过这三个圆球来计算底面的环境遮挡(Ambient Occlusion)信息。
最后,根据计算所得AO信息来动态细分网格,这样就可以生成最终的AO阴影。
综上所述,本文已经给出了移动平台上角色阴影的两种基本渲染方法及其Shader实现,在这里我并没有去分析每种渲染效果的原理,而仅是从实际出发,直接给出对应的简单实现方法。如果想要对阴影的生成方法进行深入理解,可以Google搜索其原理进行了解。对于后两种真实的动态阴影生成方法,我将在后续的blog中进行详细更新。
如果您想提高自己的技术水平,欢迎加入本站官方1号QQ群:&&,&&2号QQ群:,在群里结识技术精英和交流技术^_^
本站联系邮箱:

我要回帖

更多关于 物体内能与什么有关 的文章

 

随机推荐