u3d honey hex framework 代码解读记录(二)

// file: WorldOven.cs

/// <summary>
/// If baking were requested this function will handle starting all required processes and default settings required for initial pass
/// </summary>
/// <returns></returns>
void Update()
{
	// 如果有dirtychunk,则开始烘焙;如果已经在烘焙了,就跳过,避免重复烘焙
	// 此前world对象循环调用函数PrepareChunkData,将9个chunk加入到了dirtychunks中,所以此处dirtychunks有9个
    if (dirtyChunks.Count > 0 && baking == false)
    {
        baking = true;

        // 设置烘焙摄像机
        bakingCamera.orthographicSize = Chunk.ChunkSizeInWorld * 0.5f;
        bakingCamera.aspect = 1.0f;

        // quadBase是绘制hex的原型gameobject,他是worldoven的子节点,即quadBase.transform.parent = worldOven.transform
        quadBase.transform.localScale = new Vector3(Hex.hexTextureScale * 2.0f, Hex.hexTextureScale * 2.0f, Hex.hexTextureScale * 2.0f);

        // 绘制河流的处理,暂时跳过
        GameObject root = GameObject.Find("RiverRoot");
        foreach (Transform t in root.transform)
        {
            MeshRenderer mr = t.GetComponent<MeshRenderer>();
            if (mr != null)
            {
                riverSections.Add(mr);
            }
        }

        root = GameObject.Find("RiverSmoothener");
        foreach (Transform t in root.transform)
        {
            MeshRenderer mr = t.GetComponent<MeshRenderer>();
            if (mr != null)
            {
                riverSmoothenerSections.Add(mr);
            }
        }

        // 开始烘焙
        StartCoroutine("Baking");
    }
}

/// <summary>
/// Most complex functionality which ensures all stages of baking take place in order and that they allow world to update form time to time instead of freezing everything.
/// </summary>
/// <returns></returns>
IEnumerator Baking()
{
	// croutinhelper是一个协程帮助函数,主要用来判断协程执行时间是否过长,如果过长,则暂停执行,下一帧再继续执行
    // 此函数很长,所以本次只看PreparationStage,BakingMixerStage和BakingHeightStage三个阶段
    CoroutineHelper.StartTimer();

    while (dirtyChunks.Count > 0)
    {
        //setup chunk scene in order required by blending
        PreparationStage();
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        // Mixer
        //This texture later defines how different hexes blend each other. It bakes highest domination value taking into account distance from hex center and mixer texture.
        //Uses "Blending Max" which produces result by comparing previous color value with new result from shader:
        //R = R1 > R2 ? R1 : R2;
        //G = G1 > G2 ? G1 : G2;
        //B = B1 > B2 ? B1 : B2;
        //A = A1 > A2 ? A1 : A2;
        BakingMixerStage();
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        // Height
        // defines how high terrain is. At this stage we blend using Mixer textures and previously prepared sum of mixers. By comparing both we know how strong our hex is within this point
        // Height is written to R, While writting strenght is based on A channel
        // 相机的orthographicSize是视图的高度的一半,所以此处乘以0.5
        bakingCamera.orthographicSize = Chunk.ChunkSizeInWorld * 0.5f;
        BakingHeightStage();
        BlurTexture(heightRT, 1, 1, 1);
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        // Offset heights are baked using the same settings
        //small offset allows with comparison find shadow borders ensuring its sharpness
        //big offset ensures shadow body and coverage in irregular terrain and data artifacts
        bakingCamera.transform.localPosition = lightSourceDirection.normalized * 0.15f;  //small offset for shadow detail
        BakeTo(ref heightRTOffset1, 1);
        BlurTexture(heightRTOffset1, 1, 1, 1);
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        bakingCamera.transform.localPosition = lightSourceDirection.normalized * 0.3f; //higher offset to get shadow body
        BakeTo(ref heightRTOffset2, 1);
        BlurTexture(heightRTOffset2, 1, 1, 1);
        bakingCamera.transform.localPosition = new Vector3(0.0f, 0.0f, 0.0f);
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        shadowsAndHeightRT = ProduceShadowsAndHeightTexture(heightRT, heightRTOffset1, heightRTOffset2);
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        // Bake Diffuse
        foreach (MeshRenderer mr in hexOutlineCollection)
        {
            Vector3 pos = mr.transform.localPosition;
            pos.z = 5;
            mr.transform.localPosition = pos;
        }

        ReorderHexesPlacingWaterOnTop();
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        BakingDiffuseStage();
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        //turn off everything from camera view
        foreach (MeshRenderer mr in quadCollection)
        {
            if (mr.GetComponent<Renderer>().material != null) { GameObject.Destroy(mr.GetComponent<Renderer>().material); }
            mr.gameObject.SetActive(false);
        }
        foreach (MeshRenderer mr in hexOutlineCollection)
        {
            mr.gameObject.SetActive(false);
        }

        // Copy Height to Texture2D (ARGB) because we cant render directly to Alpha8.
        Texture2D texture;
        RenderTexture.active = shadowsAndHeightRT;
        texture = new Texture2D(Chunk.TextureSize >> 1, Chunk.TextureSize >> 1, TextureFormat.ARGB32, false);
        texture.wrapMode = TextureWrapMode.Clamp;
        texture.ReadPixels(new Rect(0, 0, Chunk.TextureSize >> 1, Chunk.TextureSize >> 1), 0, 0);
        texture.Apply();                

        //Convert height to Alpha8, its reasonably cheap and good even uncompressed format. Not compressed format saves us form artifacts near shaped water borders and mountain tops
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }
        Texture2D gScale = new Texture2D(Chunk.TextureSize >> 1, Chunk.TextureSize >> 1, TextureFormat.Alpha8, false);
        gScale.wrapMode = TextureWrapMode.Clamp;
        Color32[] data = texture.GetPixels32();

        gScale.SetPixels32(data);
        gScale.Apply();

        gScale.name = "Height" + currentChunk.position;

        //if this is chunk refresh we need to destroy old texture soon
        if (currentChunk.height != null) currentChunk.texturesForCleanup.Add(currentChunk.height);
        currentChunk.height = gScale;

        //source texture will not be used anymore
        GameObject.Destroy(texture);
        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        //Render shadow and light to scaled down texture and copy it to Texture2D
        int scaleDownPower = 4; //scaling it down 4 powers will resize e.g.: from 2048 to 128 making it marginally small
        RenderTexture shadowTarget = RenderTargetManager.GetNewTexture(Chunk.TextureSize >> scaleDownPower, Chunk.TextureSize >> scaleDownPower);
        Graphics.Blit(shadowsAndHeightRT, shadowTarget);
        RenderTexture.active = shadowTarget;
        texture = new Texture2D(Chunk.TextureSize >> scaleDownPower, Chunk.TextureSize >> scaleDownPower, TextureFormat.RGB24, false);
        texture.wrapMode = TextureWrapMode.Clamp;
        texture.ReadPixels(new Rect(0, 0, Chunk.TextureSize >> scaleDownPower, Chunk.TextureSize >> scaleDownPower), 0, 0);
        texture.Apply();

      //  SaveImage.SaveJPG(texture, currentChunk.position.ToString() + "h", randomIndex.ToString());

        texture.Compress(false); //rgb24 will compress to 4 bits per pixel.
        texture.Apply();

        //if this is chunk refresh we need to destroy old texture soon
        if (currentChunk.shadows != null) currentChunk.texturesForCleanup.Add(currentChunk.shadows);
        currentChunk.shadows = texture;
        RenderTexture.active = null;
        shadowTarget.Release();

        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        // Copy Diffuse to Texture2D
        RenderTexture.active = diffuseRT;
        texture = new Texture2D(Chunk.TextureSize, Chunk.TextureSize, TextureFormat.RGB24, false);
        texture.wrapMode = TextureWrapMode.Clamp;
        texture.ReadPixels(new Rect(0, 0, Chunk.TextureSize, Chunk.TextureSize), 0, 0);

        texture.name = "Diffuse" + currentChunk.position;
        //if this is chunk refresh we need to destroy old texture soon
        if (currentChunk.diffuse != null) currentChunk.texturesForCleanup.Add(currentChunk.diffuse);
        currentChunk.diffuse = texture;
     //   SaveImage.SaveJPG(texture, currentChunk.position.ToString() + "d", randomIndex.ToString());
        currentChunk.CompressDiffuse();

        RenderTexture.active = null;

        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

        bakingCamera.targetTexture = null;
        currentChunk.CreateGameObjectWithTextures();
        dirtyChunks.RemoveAt(0);

        World.GetInstance().ReadyToPolishChunk(currentChunk);
        RenderTargetManager.ReleaseAll();

        if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

    }

    if (World.GetInstance().StartPolishingWorld())
    {

        MeshRenderer[] rArray = GetComponentsInChildren<MeshRenderer>(true) as MeshRenderer[];
        foreach (MeshRenderer r in rArray)
        {
            if (r.material != null)
            {
                GameObject.Destroy(r.material);
            }
        }

        RenderTargetManager.DestroyAllUnusedTextures();
        Cleanup();
        DestroyObject(gameObject);
        instance = null;
    }
}

/// <summary>
/// Preparation of the scene for baking process
/// </summary>
/// <returns></returns>
void PreparationStage()
{
    displayCollection = new Dictionary<Hex, MeshRenderer>();

    // 取第一个dirtychunk,baking函数是一个循环,每次都处理第一个chunk,处理完之后,
    // 将第一个dirtychunk移除,这样来处理完所有的chunk
    currentChunk = dirtyChunks[0];

    Chunk c = currentChunk;
    Rect r = c.GetRect();
    Vector2 center = r.center;

    // hexesCovered是在函数PrepareChunkData中设置,表示当前chunk所包含的所有hex的字典
    // 将字典转换为数组,并且按照hex的顺序排序,hex的顺序是在GetHexDefinition函数中随机生成的
    List<Hex> hexes = c.hexesCovered.Values.ToList();
    hexes = hexes.OrderBy(x => x.orderPosition).ToList();

    foreach (KeyValuePair<Vector3i, Hex> pair in c.hexesCovered)
    {
    	// 获取一个空闲的meshrender, 此处是从quadBase拷贝一个gameObject,然后返回它的meshrender
        MeshRenderer mr = GetFreeRenderer();
        Hex h = pair.Value;
        // 因为meshrender是从quadBase拷贝来的,所以他的gameobject也是worldOven的子节点,
        // 所以localPostion需要设置为相对坐标
        Vector2 pos = h.GetWorldPosition() - center;

        int index = hexes.IndexOf(h); // index就是顺序

        // hexTexutreScale定义了texture覆盖hex的比例,因为它的定义是比例乘以半径,所以这里要乘以2
        mr.transform.localScale = Vector3.one * Hex.hexTextureScale * 2f;
        mr.transform.localPosition = new Vector3(pos.x, pos.y, 20 + index * 5);
        // rotationAngle也是在函数GetHexDefinition中随机生成的
        mr.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, h.rotationAngle);

        mr.material.mainTexture = h.terrainType.diffuse;
        // 添加到显示字典中,这样meshrender和hex就通过位置(vector3i)对应起来了
        displayCollection[h] = mr;

        // outline就是每个hex的暗灰色的边
        //add hex outlines behind camera for later use
        MeshRenderer outlineMr = GetFreeOutlineRenderer();
        // z轴是负数,渲染的planez轴为0,所以暂时看不到outline
        outlineMr.transform.localPosition = new Vector3(pos.x, pos.y, -5);
    }

    // 准备河流信息,暂时跳过
    PrepareRivers(new Vector3(-center.x, -center.y, 10));
}

/// <summary>
/// Preparation and render of the global mixer texture containing summary of the territorial fights between hexes
/// </summary>
/// <returns></returns>
void BakingMixerStage()
{
	// 在函数PreparationStage中,displayCollection包含了当前chunk中所有的hex的meshrender
    //Hexes
    foreach (KeyValuePair<Hex, MeshRenderer> pair in displayCollection)
    {
        // 清除掉旧的material
        if (pair.Value.material != null) { GameObject.Destroy(pair.Value.material); }

        // mixerMaterial 是worldoven这个prefab里面带的一个材质,他包含了一个叫1MixerSharder的shader,后面再来解读它
        // 此处应该是有set方法,所以并不是引用mixerMaterial,而是从mixerMaterial拷贝了一个新的material
        pair.Value.material = mixerMaterial;
        Material m = pair.Value.material;
        // 设置名字
        m.name = "mixerMaterialAT" + currentChunk.position + "FOR" + pair.Key.position;
        // 设置主贴图
        m.mainTexture = pair.Key.terrainType.mixer;
    }

    // 河流数据设置,暂时跳过
    //river shape background
    foreach (MeshRenderer river in riverSmoothenerSections)
    {
        if (river.material != null) { GameObject.Destroy(river.material); }

        river.material = mixerMaterial;
        Material m = river.material;
        m.name = "mixerMaterialAT" + currentChunk.position + "FORriverSmooth" + riverSmoothenerSections.IndexOf(river);
        m.SetFloat("_Centralization", 0.0f);
        m.mainTexture = riverSmoothenerTexture;

    }

    // 河流数据设置,暂时跳过
    //River
    TerrainDefinition riverDef = TerrainDefinition.definitions.Find(o => o.source.mode == MHTerrain.Mode.IsRiverType);
    Texture riverMixer = riverDef.mixer;
    foreach (MeshRenderer river in riverSections)
    {
        if (river.material != null) { GameObject.Destroy(river.material); }

        river.material = mixerMaterial;

        Material m = river.material;
        m.name = "mixerMaterialAT" + currentChunk.position + "FORriver" + riverSmoothenerSections.IndexOf(river);
        m.SetFloat("_Centralization", 0.0f);
        m.mainTexture = riverMixer;
    }

    // mixer render texture, 释放旧的渲染贴图
    if (mixerRT != null) mixerRT.Release();

    // 按照当前chunk的大小获取一个新的渲染贴图,作为bakingCamera的渲染目标
    mixerRT = RenderTargetManager.GetNewTexture(Chunk.TextureSize, Chunk.TextureSize, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default, 1);
    bakingCamera.targetTexture = mixerRT;

    // 执行渲染
    bakingCamera.Render();
    RenderTexture.active = null;
}

/// <summary>
/// Preparation and render of the height texture
/// </summary>
/// <returns></returns>
void BakingHeightStage()
{
    //Hexes
    foreach (KeyValuePair<Hex, MeshRenderer> pair in displayCollection)
    {
        // 将之前BakingMixerStage阶段的材质都释放掉
        if (pair.Value.material != null) { GameObject.Destroy(pair.Value.material); }

        // 设置为heightMaterial,它包含了2HeightShader.shader,后面解读
        pair.Value.material = heightMaterial;
        Material m = pair.Value.material;
        // 主贴图为地形定义中的height贴图
        m.SetTexture("_MainTex", pair.Key.terrainType.height);
        // 上次作为主贴图的mixer赋值给了_Mixer
        m.SetTexture("_Mixer", pair.Key.terrainType.mixer);
        // 设置全局mixer为之前BakingMixerStage阶段生成的渲染贴图
        m.SetTexture("_GlobalMixer", mixerRT);
    }

    // 河流数据设置,暂时跳过
    //river
    TerrainDefinition riverDef = TerrainDefinition.definitions.Find(o => o.source.mode == MHTerrain.Mode.IsRiverType);
    Texture riverHeight = riverDef.height;
    Texture riverMixer = riverDef.mixer;
    foreach (MeshRenderer river in riverSections)
    {
        if (river.material != null) { GameObject.Destroy(river.material); }

        river.material = riverHeightMaterial;
        Material m = river.material;
        m.SetFloat("_Centralization", 0.0f);
        m.SetTexture("_MainTex", riverHeight);
        m.SetTexture("_Mixer", riverMixer);
        m.SetTexture("_GlobalMixer", mixerRT);
    }

    // 河流数据设置,暂时跳过
    //Smoothener will neutralize a bit area where river would be located to avoid artefacts as much as possible
    foreach (MeshRenderer river in riverSmoothenerSections)
    {
        if (river.material != null) { GameObject.Destroy(river.material); }

        river.material = riverSmoothenerMaterial;
        Material m = river.material;
        m.SetTexture("_MainTex", riverSmoothenerTexture);
    }

    // 释放旧的高度渲染贴图
    if (heightRT != null) heightRT.Release();

    // 贴图大小是前一阶段的四分之一,采用拉伸模式渲染高度贴图
    heightRT = RenderTargetManager.GetNewTexture(Chunk.TextureSize >> 1, Chunk.TextureSize >> 1, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default, 1);
    heightRT.wrapMode = TextureWrapMode.Clamp;
    bakingCamera.targetTexture = heightRT;            

    bakingCamera.Render();
}

/// <summary>
/// Simple function which adds a bit of the blur to texture. used mostly by the height textures to avoid artifacts
/// </summary>
/// <param name="texture"></param>
/// <param name="downSample"></param>
/// <param name="size"></param>
/// <param name="interations"></param>
/// <returns></returns>
// 给贴图加上blur效果,用在高度图上面,可以使高度变化趋势变得比较平滑,看上去更加自然,
// 解读本函数需要分析mobileblurshader,后续解读时再仔细对比和解读这个函数。
// 目前简单看起来就是设置采样范围,使用水平和垂直模糊
void BlurTexture(RenderTexture texture, int downSample, int size, int interations)
{
    float widthMod = 1.0f / (1.0f * (1 << downSample));

    // 需要后面分析blurMaterial带的sharder
    Material material = new Material(blurMaterial);
    material.SetVector("_Parameter", new Vector4(size * widthMod, -size * widthMod, 0.0f, 0.0f));
    texture.filterMode = FilterMode.Bilinear;

    int rtW = texture.width >> downSample;
    int rtH = texture.height >> downSample;

    // downsample
    RenderTexture rt = RenderTargetManager.GetNewTexture(rtW, rtH, 0, texture.format);
    rt.filterMode = FilterMode.Bilinear;            

    Graphics.Blit(texture, rt, material, 0);

    for (int i = 0; i < interations; i++)
    {
        float iterationOffs = (i * 1.0f);
        material.SetVector("_Parameter", new Vector4(size * widthMod + iterationOffs, -size * widthMod - iterationOffs, 0.0f, 0.0f));

        // vertical blur
        RenderTexture rt2 = RenderTargetManager.GetNewTexture(rtW, rtH, 0, texture.format);
        rt2.filterMode = FilterMode.Bilinear;

        Graphics.Blit(rt, rt2, material, 1);
        RenderTargetManager.ReleaseTexture(rt);
        rt = rt2;

        // horizontal blur
        rt2 = RenderTargetManager.GetNewTexture(rtW, rtH, 0, texture.format);
        rt2.filterMode = FilterMode.Bilinear;

        Graphics.Blit(rt, rt2, material, 2);
        RenderTargetManager.ReleaseTexture(rt);
        rt = rt2;
    }

    GameObject.Destroy(material);

    Graphics.Blit(rt, texture);

    RenderTargetManager.ReleaseTexture(rt);
}

时间: 2024-10-06 03:44:00

u3d honey hex framework 代码解读记录(二)的相关文章

Jsoup代码解读之二-DOM相关对象

Jsoup代码解读之二-DOM相关对象 之前在文章中说到,Jsoup使用了一套自己的DOM对象体系,和Java XML API互不兼容.这样做的好处是从XML的API里解脱出来,使得代码精炼了很多.这篇文章会说明Jsoup的DOM结构,DOM的遍历方式.在下一篇文章,我会并结合这两个基础,分析一下Jsoup的HTML输出功能. DOM结构相关类 我们先来看看nodes包的类图: 这里可以看到,核心无疑是Node类. Node类是一个抽象类,它代表DOM树中的一个节点,它包含: 父节点parent

目标跟踪系列十二:Exploiting the Circulant Structure of Tracking-by-detection with Kernels代码关键记录

代码关键点记录:成功不远了!O(∩_∩)O 哈哈~ 1. Input : load_video_info.m 读入视频文件groundtruth_rect.txt(里面是4个一组的点,x,y, width ,height ?),得到有用的参数: target_sz = [ground_truth(1,4), ground_truth(1,3)];  得到的是目标的尺寸(这里好像是第一张的,也就是一样大) 这个值会用来计算空间带宽的值.通常一个m*n的目标,它的空间带宽为 sqrt(m*n)/16

YYModel 源码解读(二)之NSObject+YYModel.h (1)

本篇文章主要介绍 _YYModelPropertyMeta 前边的内容 首先先解释一下前边的辅助函数和枚举变量,在写一个功能的时候,这些辅助的东西可能不是一开始就能想出来的,应该是在后续的编码过程中 逐步添加的. #define force_inline __inline__ __attribute__((always_inline)) 这行代码用到了C语言的内联函数 内联函数: 是用inline修饰的函数,内联函数在代码层次看和普通的函数结构一样,却不具备函数的性质,内联函数不是在调用时发生控

学习日记(2.19 BP神经网络完整代码解读)

BP网络实现手写数字识别代码解读 1.添加偏置 #添加偏置 temp=np.ones([X.shape[0],X.shape[1]+1]) temp[:,0:-1]=X X=temp np.ones()函数 numpy.ones()函数的功能是返回一个全都是1的N维数组,其中shape(用来指定返回数组的大小).dtype(数组元素的类型).order(是否以内存中的C或Fortran连续(行或列)顺序存储多维数据).后两个参数都是可选的,一般只需设定第一个参数. shape[]的功能是: 0查

Windows API 编程学习记录&lt;二&gt;

恩,开始写Windows API编程第二节吧. 上次介绍了几个关于Windows API编程最基本的概念,但是如果只是看这些概念,估计还是对Windows API不是很了解.这节我们就使用Windows API 让大家来了解下Windows API的用法. 第一个介绍的Windows API 当然是最经典的MessageBox,这个API 的作用就是在电脑上显示一个对话框,我们先来看看这个API的定义吧: int WINAPI MessageBox(HWND hWnd, LPCTSTR lpTe

为代码减负之&lt;二&gt;存储过程(SQL)

在上篇博客中介绍到了触发器的使用,并且其中也提到了触发器是个特殊的存储过程,那么什么是存储过程呢?他们 两个又到底有什么区别呢? 其实最主要的区别就是,触发器是当满足条件时系统自动执行的,而存储过程是手动调用的. 简单介绍 什么是存储过程? 定义:将常用的或很复杂的工作,预先用SQL语句写好并用一个指定的名称存储起来,用户通过指定存储过程的名字 并给出参数(如果该存储过程带有参数)来调用它. 讲到这里,可能有人要问:这么说存储过程不就是一堆SQL语句而已吗?那么存储过程与一般的SQL语句有什么区

Jsoup代码解读之四-parser

Jsoup代码解读之四-parser 作为Java世界最好的HTML 解析库,Jsoup的parser实现非常具有代表性.这部分也是Jsoup最复杂的部分,需要一些数据结构.状态机乃至编译器的知识.好在HTML语法不复杂,解析只是到DOM树为止,所以作为编译器入门倒是挺合适的.这一块不要指望囫囵吞枣,我们还是泡一杯咖啡,细细品味其中的奥妙吧. 基础知识 编译器 将计算机语言转化为另一种计算机语言(通常是更底层的语言,例如机器码.汇编.或者JVM字节码)的过程就叫做编译(compile).编译器(

【dlib代码解读】人脸检测器的训练【转】

转自:http://blog.csdn.net/elaine_bao/article/details/53046542 版权声明:本文为博主原创文章,转载请注明. 目录(?)[-] 综述 代码解读 step by step 1 预处理阶段 11 载入训练集测试集 12 图片上采样 13 镜像图片 2 训练阶段 21 定义scanner用于扫描图片并提取特征 22 设置scanner扫描窗口大小 23 定义trainer用于训练人脸检测器 24 训练生成人脸检测器 25 测试 3 tips 31

iOS开发CoreAnimation解读之二——对CALayer的分析

iOS开发CoreAnimation解读之二——对CALayer的分析 一.UIView中的CALayer属性 1.Layer专门负责view的视图渲染 2.自定义view默认layer属性的类 二.几种系统的Layer类 1.CAEmitterLayer 2.CAGradientLayer 3.CAEAGLLayer 4.CAReplicatorLayer 5.CAScrollLayer 6.CAShapeLayer 7.CATextLayer 8.CATiledLayer 9.CATrans