移植UE4的模型操作到Unity中

  最近在Unity上要写一个东东,功能差不多就是在Unity编辑器上的旋转,移动这些,在手机上也能比较容易操作最好,原来用Axiom3D写过一个类似的,有许多位置并不好用,刚好在研究UE4的源码,在模型操作上,很多位置都解决了,其实大家可以对比下,在UE4与Unity中,UE4的如移动和旋转都要正确和好用。

  如下是根据UE4中简单移植过来的效果图,差不多已经够用,UE4相关源码主要在EditorViewportClient与UnrealWidget。

  

  介绍一下这个组件主要功能。

  1. 模型本地空间与世界空间二种模式。

  2. 根据情况动态生成操作模型,如在移动模型时,选择的轴变色,旋转时,视角与模型的方向产生不同模型。

  3. 移动根据鼠标平面映射到对应移动平面,点保存上轴上距离不变(作为对比,可以看到Unity上长距离移动,鼠标位置在轴上的位置位移会不断拉大)。

  4. 不管模型与摄像机的距离,旋转与移动操作都是合适的大小。

  5. 旋转方向的确定,简单说,就是在旋转时,如果用鼠标移动来确定旋转的方向,这个问题看似简单,我以前就没搞出来。

  6. 在移动本台,我们需要更方便的操作,所以在移动平台会有些操作,如更容易选中,生成的模型会更大等。

  最后,有一些,如箭头模型,选择旋转与移动轴的算法以前考虑过,就没用UE4本身的,如果感觉有问题,自己去移植UE4的。其中旋转因为移动平台易用性,就设定了一个值,如在我这设定的是10,就是每次只旋转10度。

  简单分析一下,UE4里相关思路。

  其中移动的算法思路非常赞,比如我们要移动X轴,那么我们对应在法线为Y或是法线为Z轴上的平面都可以,通过摄像机的方向与这二个平面的夹角,在这如果摄像机的方向与法线Y平面的角大于与法线Z平面的角,那么我们选择法线Y平面的面做映射面,而Z向量作偏向轴方向,什么意思了,我们鼠标是在二维面上移动的,但是对应的只在X轴上移动,那么我们在法线Y平面上的映射向量需要去掉在Z向量上偏向量的影响。如下是移动的主要代码,每步我加了注释,其中一些比较常用如投影,向量减向量在某向量上的投影的意义要记清,当初我也是看到这,就一下想通这个算法的思路了。  

    /// <summary>
    /// FWidget::GetAbsoluteTranslationDelta
    /// 算法思想,如果移动X轴,选取以Y轴或是Z轴为法线并过模型上的面,鼠标移动映射在这个面上。
    /// 其中,如果选择Y轴面,要去掉Z轴上运动值,参看NormalToRemove
    /// </summary>
    /// <param name="inParams"></param>
    /// <returns></returns>
    public Vector3 GetAbsoluteTranslationDelta(AbsoluteMovementParams inParams)
    {
        //鼠标移动的位置 对应的面,请看GetAxisPlaneNormalAndMask方法
        Plane movementPlane = new Plane(inParams.PlaneNormal, inParams.Position);
        //估算鼠标点击在模型上的位置(点击射线方向)
        Vector3 eyeVector = inParams.EyePos + inParams.PixelDir * (inParams.Position - inParams.EyePos).magnitude;
        //模型的世界位置
        Vector3 requestedPositon = inParams.Position;

        //点击方向与面的夹角
        float dotPlaneNormal = Vector3.Dot(inParams.PixelDir, inParams.PlaneNormal);
        //摄像机方向与面的夹角不为90度
        if (Mathf.Abs(dotPlaneNormal) > 0.00001)
        {
            //摄像机到点击位置 与 面的交点 ,把requestedPositon映射到面上位置
            requestedPositon = LinePlaneIntersection(inParams.EyePos, eyeVector, movementPlane);
        }
        //拖动的增量(都在movementPlane上,二点相差)
        var deltaPosition = requestedPositon - inParams.Position;
        //保存最开始点击下去得到的偏移
        Vector3 offset = GetAbsoluteTranslationInitialOffset(requestedPositon, inParams.Position);
        //去掉最开始本身的偏移
        deltaPosition -= initialOffset;
        //.Log("delta:" + deltaPosition);
        //去掉deltaPosition到NormalToRemove上投影 outDrag与NormalToRemove 互相垂直,outDrag+NormalToRemove = deltaPosition
        float movementAxis = Vector3.Dot(deltaPosition, inParams.NormalToRemove);
        Vector3 outDrag = deltaPosition - inParams.NormalToRemove * movementAxis;
        //Debug.Log("outDrag:" + outDrag);
        //get the distance from the original position to the new proposed position
        //Vector3 deltaFromStart = inParams.Position + outDrag - initialPosition;
        //模型到摄像机方向
        Vector3 eyeToNewPosition = inParams.Position + outDrag - inParams.EyePos;
        //模型到摄像机方向与摄像机方向 夹角大于90度
        float behindDot = Vector3.Dot(eyeToNewPosition, inParams.CameraDir);
        if (behindDot <= 0)
        {
            outDrag = Vector3.zero;
        }
        return outDrag;
    }

FWidget::GetAbsoluteTranslationDelta

  移动的算法差不多就是这样,其中如何生成移动模型就不拉出来,后面会给出源代码,大家自己去找。至于如何找到移动模型对应的X,Y,Z轴,或是全部移动,算法以前写过,求得二射线相隔最近的二点,然后根据二点的长度判断是否认为相交,在代码文件上的GetAxisType,具体大家去看。

  旋转时,我们根据摄像机到模型的向量分别计算对应的XYZ轴上正负向量,再分别生成如X轴上对应YZ平面的90度弧形,顺便我们得到每个对应平面在对应屏幕上的方向,这样我们在屏幕上移动就能正确的对应模型应该的旋转方向,列出其中相关代码,更详细的解释请看函数对应的注释。  

    #region 渲染旋转
    public void Render_Rotate()
    {
        if (currentAxis == AxisType.None)
        {
            Render_RotateArc();
        }
        else
        {
            Render_RotateAll();
        }
    }

    //旋转模式下,生成三个面的旋转模型
    public void Render_RotateArc()
    {
        Vector3 toWidget = ((this.transform.position - Camera.main.transform.position)).normalized;
        Vector3 XAxis = coordSystem * Vector3.right;
        Vector3 YAxis = coordSystem * Vector3.up;
        Vector3 ZAxis = coordSystem * Vector3.forward;

        //画对应的旋转的90度面
        var redMesh = DrawRotationArc(AxisType.X, this.transform.position, ZAxis, YAxis, 0, Mathf.PI / 2.0f, toWidget, Color.red, ref xAxisDir);
        var greenMesh = DrawRotationArc(AxisType.Y, this.transform.position, XAxis, ZAxis, 0, Mathf.PI / 2.0f, toWidget, Color.green, ref yAxisDir);
        var blueMesh = DrawRotationArc(AxisType.Z, this.transform.position, XAxis, YAxis, 0, Mathf.PI / 2.0f, toWidget, Color.blue, ref zAxisDir);
        //分别合并面与线,合成一个SubMesh时,要求MeshTopology与材质一样
        var faceMesh = CombineMesh(true, redMesh.FaceMesh, greenMesh.FaceMesh, blueMesh.FaceMesh);

        //var lineMesh = CombineMesh(true, redMesh.LineMesh, greenMesh.LineMesh, blueMesh.LineMesh);
        float x = Mathf.Sign(Vector3.Dot(toWidget, bLocation ? axisTransform.right : Vector3.right));
        float y = Mathf.Sign(Vector3.Dot(toWidget, bLocation ? axisTransform.up : Vector3.up));
        float z = Mathf.Sign(Vector3.Dot(toWidget, bLocation ? axisTransform.forward : Vector3.forward));
        var redLineMesh = CreateLine(Vector3.zero, -XAxis * innerRadius * x, Color.red);
        var greenLineMesh = CreateLine(Vector3.zero, -YAxis * innerRadius * y, Color.green);
        var blueLineMesh = CreateLine(Vector3.zero, -ZAxis * innerRadius * z, Color.blue);
        var lineMesh = CombineMesh(true, redLineMesh, greenLineMesh, blueLineMesh);

        //合并面与线,分别对应一个SubMesh,可以用不同MeshTopology与材质
        meshFilter.mesh = CombineMesh(false, faceMesh, lineMesh);
        //给每个SubMesh对应材质
        meshRender.sharedMaterials = new Material[2] { faceMat, lineMat };
    }

    /// <summary>
    /// FWidget::DrawRotationArc 渲染选择某个轴后的对应模型,360度的面
    /// </summary>
    public void Render_RotateAll()
    {
        Vector3 toWidget = (this.transform.position - Camera.main.transform.position).normalized;
        Vector3 XAxis = coordSystem * Vector3.right;    // Quaternion.Inverse(coordSystem) *
        Vector3 YAxis = coordSystem * Vector3.up;       //
        Vector3 ZAxis = coordSystem * Vector3.forward;  // 

        float adjustDeltaRotation = bLocation ? -totalDeltaRotation : totalDeltaRotation;
        float absRotation = Mathf.Abs(totalDeltaRotation) % 360.0f;
        float angleRadians = absRotation * Mathf.Deg2Rad;

        float startAngle = adjustDeltaRotation < 0.0f ? -angleRadians : 0.0f;
        float filledAngle = angleRadians;

        LineFaceMesh meshRotation = null;
        LineFaceMesh meshAll = null;
        //画对应的旋转的90度面
        if (currentAxis == AxisType.X)
        {
            meshRotation = DrawRotationArc(AxisType.X, this.transform.position, ZAxis, YAxis, startAngle, startAngle + filledAngle, toWidget, Color.red);
            meshAll = DrawRotationArc(AxisType.X, this.transform.position, ZAxis, YAxis, startAngle + filledAngle, startAngle + 2.0f * Mathf.PI, toWidget, Color.yellow);
        }
        else if (currentAxis == AxisType.Y)
        {
            meshRotation = DrawRotationArc(AxisType.Y, this.transform.position, XAxis, ZAxis, startAngle, startAngle + filledAngle, toWidget, Color.green);
            meshAll = DrawRotationArc(AxisType.Y, this.transform.position, XAxis, ZAxis, startAngle + filledAngle, startAngle + 2.0f * Mathf.PI, toWidget, Color.yellow);
        }
        else if (currentAxis == AxisType.Z)
        {
            meshRotation = DrawRotationArc(AxisType.Z, this.transform.position, XAxis, YAxis, startAngle, startAngle + filledAngle, toWidget, Color.blue);
            meshAll = DrawRotationArc(AxisType.Z, this.transform.position, XAxis, YAxis, startAngle + filledAngle, startAngle + 2.0f * Mathf.PI, toWidget, Color.yellow);
        }

        meshFilter.mesh = CombineMesh(false, meshRotation.FaceMesh, meshAll.FaceMesh);
        //给每个SubMesh对应材质
        meshRender.sharedMaterials = new Material[2] { lineMat, faceMat };
    }

    public LineFaceMesh DrawRotationArc(AxisType type, Vector3 inLocation, Vector3 axis0, Vector3 axis1, float inStartAngle, float inEndAngle, Vector3 toWidget, Color32 color)
    {
        Vector2 outAxis = new Vector2();
        return DrawRotationArc(type, inLocation, axis0, axis1, inStartAngle, inEndAngle, toWidget, color, ref outAxis);
    }
    ///X轴上,我们渲染YZ平面,先确定在摄像机->模型在Y轴与Z轴上的方向,再确定这个平面对应在屏幕上的方向
    public LineFaceMesh DrawRotationArc(AxisType type, Vector3 inLocation, Vector3 axis0, Vector3 axis1, float inStartAngle, float inEndAngle, Vector3 toWidget, Color32 color, ref Vector2 outAxisDir)
    {
        //确定采用轴的正向还是反向
        bool bMirrorAxis0 = Vector3.Dot(axis0, toWidget) <= 0.0f;
        bool bMirrorAxis1 = Vector3.Dot(axis1, toWidget) <= 0.0f;

        Vector3 renderAxis0 = bMirrorAxis0 ? axis0 : -axis0;
        Vector3 renderAxis1 = bMirrorAxis1 ? axis1 : -axis1;
        //画90度弧形
        var mesh = DrawThickArc(renderAxis0, renderAxis1, inStartAngle, inEndAngle, toWidget, color);

        //确定屏幕上对应方向
        float direction = (bMirrorAxis0 ^ bMirrorAxis1) ? -1.0f : 1.0f;
        var axisSceen0 = ScreenToPixel(this.transform.position + renderAxis0 * 64);
        var axisSceen1 = ScreenToPixel(this.transform.position + renderAxis1 * 64);

        outAxisDir = ((axisSceen1 - axisSceen0) * direction).normalized;
        return mesh;
    }

    //世界点转成屏幕对应的像素位置
    public Vector2 ScreenToPixel(Vector3 pos)
    {
        Vector4 loc = pos;
        loc.w = 1;
        //MVP 后的位置,其值在 DX/OpenGL 范围各不相同
        Vector4 mvpLoc = Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix * loc;
        //四维数据转到三维,简单来说,X,Y,Z限定范围到DX/OpenGL所定义的包围圈中
        float InvW = 1.0f / mvpLoc.w;
        //这里是在DX下的范围,由[-1,1]映射到[0,1]中
        var x = (0.5f + mvpLoc.x * 0.5f * InvW) * Camera.main.pixelWidth;
        var y = (0.5f - mvpLoc.y * 0.5f * InvW) * Camera.main.pixelHeight;

        return new Vector2(x, y);
    }

    /// <summary>
    /// 动态生成以axis0和axis1组成的平面,以axis0为0度,画从inStartAngle到inEndAngle弧形
    /// </summary>
    public LineFaceMesh DrawThickArc(Vector3 axis0, Vector3 axis1, float inStartAngle, float inEndAngle, Vector3 toWidget, Color32 color)
    {
        LineFaceMesh lineFace = new LineFaceMesh();
        Mesh mesh = lineFace.FaceMesh;
        //Mesh lineMesh = lineFace.LineMesh;

        int numPoints = (int)(circleSide * (inEndAngle - inStartAngle) / (Mathf.PI / 2.0f)) + 1;
        Vector3 zAxis = Vector3.Cross(axis0, axis1);

        Vector3[] posArray = new Vector3[2 * numPoints + 2];
        Color32[] colorArray = new Color32[2 * numPoints + 2];
        Vector2[] uvArray = new Vector2[2 * numPoints + 2];
        //Vector3[] linePosArray = new Vector3[4 * numPoints + 4];

        int index = 0;
        Vector3 lastVertex = Vector3.zero;
        for (int radiusIndex = 0; radiusIndex < 2; ++radiusIndex)
        {
            float radius = (radiusIndex == 0) ? outerRadius : innerRadius;
            float tcRadius = radius / (float)innerRadius;

            for (int vectexIndex = 0; vectexIndex <= numPoints; vectexIndex++)
            {
                float percent = vectexIndex / (float)numPoints;
                float angle = Mathf.Lerp(inStartAngle, inEndAngle, percent);
                float angleDeg = angle * Mathf.Rad2Deg;

                Vector3 vertexDir = Quaternion.AngleAxis(angleDeg, zAxis) * axis0;
                vertexDir.Normalize();

                float tcAngle = percent * Mathf.PI / 2;

                Vector2 tc = new Vector2(tcRadius * Mathf.Cos(angle), tcRadius * Mathf.Sin(angle));
                Vector3 vertexPos = vertexDir * radius;

                posArray[index] = vertexPos;
                uvArray[index] = tc;
                colorArray[index] = color;

                ++index;
                lastVertex = vertexPos;
            }
        }
        mesh.vertices = posArray;
        mesh.uv = uvArray;
        mesh.colors32 = colorArray;

        int innerStart = numPoints + 1;
        int[] triArray = new int[3 * 2 * numPoints];
        index = 0;
        for (int vertexIndex = 0; vertexIndex < numPoints; vertexIndex++)
        {
            triArray[index++] = vertexIndex;
            triArray[index++] = vertexIndex + 1;
            triArray[index++] = vertexIndex + innerStart;
            triArray[index++] = vertexIndex + 1;
            triArray[index++] = vertexIndex + innerStart + 1;
            triArray[index++] = vertexIndex + innerStart;
        }
        mesh.triangles = triArray;
        lineFace.LineMesh = CreateLine(Vector3.zero, zAxis * innerRadius, color);
        return lineFace;
    }

    //创建一个线段
    public Mesh CreateLine(Vector3 start, Vector3 end, Color32 color)
    {
        Mesh mesh = new Mesh();
        mesh.vertices = new Vector3[2] { start, end };
        mesh.uv = new Vector2[2] { Vector2.zero, Vector2.zero };
        mesh.colors32 = new Color32[2] { color, color };
        mesh.SetIndices(new int[] { 0, 1 }, MeshTopology.Lines, 0);
        return mesh;
    }

    //Mesh.CombineMeshes 需要已经正确的subMesh indices,而这里的mesh的indices都是从0开始,自己写个
    public Mesh CombineMesh(bool mergeSubMeshes, params Mesh[] meshs)
    {
        List<Vector3> vectors = new List<Vector3>();
        List<Vector2> uvs = new List<Vector2>();
        List<Color32> colors = new List<Color32>();
        List<int> startIndexs = new List<int>();
        int start = 0;
        int indexCount = 0;
        bool bUV = true;
        bool bColor = true;
        foreach (var mesh in meshs)
        {
            vectors.AddRange(mesh.vertices);
            uvs.AddRange(mesh.uv);
            if (mesh.uv.Length == 0)
                bUV = false;
            colors.AddRange(mesh.colors32);
            if (mesh.colors32.Length == 0)
                bColor = false;
            startIndexs.Add(start);
            start += mesh.vertexCount;
            indexCount += mesh.GetIndices(0).Length;
        }

        var combineMesh = new Mesh();
        combineMesh.SetVertices(vectors);
        if (bUV)
            combineMesh.SetUVs(0, uvs);
        if (bColor)
            combineMesh.SetColors(colors);

        combineMesh.subMeshCount = mergeSubMeshes ? 1 : meshs.Length;
        int[] allIndices = new int[indexCount];
        int autoIndex = 0;
        for (int i = 0; i < meshs.Length; i++)
        {
            var indices = meshs[i].GetIndices(0);
            int count = indices.Length;
            int[] tris = new int[count];
            for (int j = 0; j < count; j++)
            {
                allIndices[autoIndex++] = indices[j] + startIndexs[i];
                tris[j] = indices[j] + startIndexs[i];
            }
            if (!mergeSubMeshes)
                combineMesh.SetIndices(tris, meshs[i].GetTopology(0), i);
        }
        if (mergeSubMeshes)
            combineMesh.SetIndices(allIndices, meshs[0].GetTopology(0), 0);
        return combineMesh;
    }
    #endregion

渲染旋转

  因为UE4中有RHI,所以只管放入相应Rendering Command,下面会自动合并,优化,而Unity因为高度集成,相反在写这些代码时比较麻烦,如上,我本意在场景里定义一个空的模型,加上我这个脚本后就能实现相应旋转,移动的功能,不引入别的任何内容,也不生成子GameObject,所以动态生成对应的MeshFilter与MeshRenderer要考虑如下需求。

  1. 只有一个MeshFilter与MeshRender,这样我们可能要自己组装多个SubMesh.

  2. 每个轴用不同的颜色表示,并且每轴需要二种绘制方式,三角面,线条。

  3. 我们要优化渲染,需要最少的Material能完成就用最少的Material,以及最少的SubMesh.

  4. 渲染需要,深度测试通过,但是不要写入深度缓存中,不受灯光影响。

  5. 层次显示需要,面要透明,而线不需要透明。

  一般来说,每个面用不同颜色表示,在Unity中就需要不同的Material,或运行时设置Material的变量,这样每个面就不能合并显示,我们需要能利用模型本身颜色的Shader,并且要满足上面第四点,通过Unity官方提供的Unity5Shader这个项目,我们找到GUI/Text Shader,满足上面的条件,这样,生成三个轴对应的面模型时,使用颜色数据,就能合并成一个SubMesh,使用一个Material渲染,我们知道,同一个SubMesh,不可能出现一个画三角面,一个线,这样我们最少有二个SubMesh。大家对照下Render_RotateArc这个方法,结合ComBineMesh这个方法,可能有的同学会问,Unity不是本身就提供了Mesh.CombineMeshs,使用这个合并不就OK了,Mesh.CombineMeshs这个方法需要本身的SubMesh对应的Indices里索引已经是全局数据的索引才可以用的,什么意思了,我们这边生成的三个Mesh,其indices里的数据都是针对本身的vectices的索引,用CombineMeshs合并后,后面的Mesh对应的索引就错了。

  上面的这部分UE4与Unity代码几乎完全不同,需要大家自己修改成自己所需要的。

  选择旋转轴的算法没用UE4的,用的一种非常简单的方法,大致思路,找到射线与圆的二个交点,把交点转到模型空间中,查看交点的x,y,z的值,那个值接近0,就是那个轴,想具体理解可见我前文 一个简单的旋转控制器与固定屏幕位置 ,里面也有求得移动轴的算法。

  最后,说一个简单的东东,原来我一直没搞出来,不管模型与摄像机的距离,旋转与移动操作都是合适的大小,我原来求出来的值,要么就是在距离少时,显示不对,要么就是在距离远时,显示不对,而UE4给出一个简单的式子,如下面代码。

Vector4 aposition = axisTransform.position;
            aposition.w = 1;
            float w = (Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix * aposition).w;
            widgetScale = w * (4.0f / Camera.main.pixelWidth / Camera.main.projectionMatrix[0, 0]);

widgetScale

  代码完整链接 UWidget.zip,就一个文件,在Unity场景中,根节点下建立一个GameObject,把这个脚本放上面去就行,对应UI如设置 世界/本地,旋转,移动都有相应API调用。

时间: 2024-10-08 00:42:41

移植UE4的模型操作到Unity中的相关文章

blender 带贴图与颜色材质的模型,导入到Unity中

Blender初学环境:win10 x64blender 2.79unity 5.6.2 之前不论是用导出fbx,还是把 .blend文件拽入u3d中,原先的贴图就没有了研究了很多天,才发现一个我这里可用的方法 目前只是简单以cube为例,复杂的多材质模型还没试 打开blender注意,用的是blender渲染一.新建一个cube 二.展UV1.开出一个UV图像编辑器窗口2.在三维视图按Tab进入编辑模式确保选上所有,如果没有用A键3.按U键调出菜单,选第二项点确定 三.加贴图1.打开一张图(我

【Unity技巧】Unity中的优化技术

写在前面 这一篇是在Digital Tutors的一个系列教程的基础上总结扩展而得的~Digital Tutors是一个非常棒的教程网站,包含了多媒体领域很多方面的资料,非常酷!除此之外,还参考了Unity Cookie中的一个教程.还有很多其他参考在下面的链接中. 这篇文章旨在简要地说明一下常见的各种优化策略.不过对每个基础有非常深入地讲解,需要的童鞋可以自行去相关资料. 还有一些我认为非常好的参考文章: Performance Optimization for Mobile Devices

unity3d之从3ds max导入素材到unity中的设置

我们制造一个子弹的模型 1 首先设置3d max中参数,设置Customize->Units Setup Metic为厘米 2 建模,这里我们使用plane,一个平面,如图 3 然后导出 4 unity中模型设置,如图 在这里有个参数Scale Factor,这个参数很重要,设置小了,导致在unity里面可能看不到,大了又不合适. 5 创建一个material 6 创建一个shader,把这个shader赋值给刚才创建的material 这里的shader代码如下 Shader "Angr

unity中使用FingerGestures插件3.0

FingerGestures是一个unity3D插件,用来处理用户动作,手势. 译自FingerGestures官方文档 目录 FingerGestures包结构 FingerGestures例子列表 设置场景 教程:识别一个轻敲手势 教程:手势识别器 教程:轻击手势识别器 教程:拖拽手势识别器 教程:滑动手势识别器 教程:长按手势识别器 教程:缩放手势识别器 教程:旋转手势识别器 教程:自定义手势识别器 教程:识别手势事件 建议:使用.net代理事件 fingerGestures包结构 路径,

(转)【Unity技巧】Unity中的优化技术

写在前面 这一篇是在Digital Tutors的一个系列教程的基础上总结扩展而得的~Digital Tutors是一个非常棒的教程网站,包含了多媒体领域很多方面的资料,非常酷!除此之外,还参考了Unity Cookie中的一个教程.还有很多其他参考在下面的链接中. 这篇文章旨在简要地说明一下常见的各种优化策略.不过对每个基础有非常深入地讲解,需要的童鞋可以自行去相关资料. 还有一些我认为非常好的参考文章: Performance Optimization for Mobile Devices

3D语音天气球——在Unity中使用Android语音服务

转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 开篇废话: 这个项目准备分四部分介绍: 一:创建可旋转的"3D球":3D语音天气球(源码分享)--创建可旋转的3D球 二:通过天气服务,从网络获取时实天气信息并动态生成"3D球":3D语音天气球(源码分享)--通过天气服务动态创建3D球 三:Android语音服务和Unity的消息传递 四:Unity3D端和Android端的结合 前两篇文章已经介绍了如何创

解读Unity中的CG编写Shader系列3——表面剔除与剪裁模式

在上一个样例中,我们得到了由mesh组件传递的信息经过数学转换至合适的颜色区间以颜色的形式着色到物体上. 这篇文章将要在此基础上研究片段的擦除(discarding fragments)和前面剪裁.后面剪裁(front face culling and back face culling)来达到透明效果. 当一个mesh组件的信息被传递后,我们能够通过代码决定哪些部分渲染(render)出来.而哪些部分不要.这个过程就像把那些不要的部分剔除了,我们看不到他.尽管他的mesh信息还在.可是我们的G

解读Unity中的CG编写Shader系列9——镜面反射

讨论完漫反射之后,接下来肯定就是镜面反射了 在开始镜面反射shader的coding之前,要扩充一下前面提到的知识,加深理解镜面反射与漫反射的区别. 引用一下一位前人博文中的一些基础概念,特别是关于冯氏反射模型的: 平行光(directional light) 一种是从特定方向射入并只会照亮面对入射方向的物体,我们称之为平行光(directional light). 环境光(ambient light) 另一种光是来自所有方向并且会照亮所有物体,不管这些物体的朝向如何,我们称之为环境光(ambi

解读Unity中的CG编写Shader系列三

转自http://www.itnose.net/detail/6096068.html 在上一个例子中,我们得到了由mesh组件传递的信息经过数学转换至合适的颜色区间以颜色的形式着色到物体上.这篇文章将要在此基础上研究片段的擦除(discarding fragments)和前面剪裁.后面剪裁(front face culling and back face culling)来达到透明效果. 当一个mesh组件的信息被传递后,我们可以通过代码决定哪些部分渲染(render)出来,而哪些部分不要,这