ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

一种简易的球面图包裹物体AO实现

2021-09-03 21:03:51  阅读:403  来源: 互联网

标签:uv 球面 float Mathf AO 简易 1f public float3


最近翻到了一篇2005年siggraph的文章:Ambient Occlusion Fields Janne Kontkanen

https://files-cdn.cnblogs.com/files/hont/kontkanen2005i3d_paper.zip

 

感觉大概思路是通过球体去拟合包裹范围内的模型,得到一个近似点计算AO。

原文方法比较复杂,用了2个Cubemap7个参数。

这里先实现一个简单的,以后有需求再研究,用到了之前CapsuleAO的代码:

https://www.cnblogs.com/hont/p/14965382.html

 

主要使用环境贴图包裹物体,拿到最近点位置计算AO,效果如下:

 

第一步使用全景图绘制一圈射线,然后检测最近距离。

 

第二步生成全景图,交给Shader部分处理。

 

代码如下,CSharp部分:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AOFields : MonoBehaviour
{
    [Range(0.01f, 0.1f)] public float xPrecise = 0.1f;//相对全景图x轴的射线精度
    [Range(0.01f, 0.1f)] public float yPrecise = 0.1f;//相对全景图y轴的射线精度
    public float radius = 1f;

    public float[] r;//烘焙的最近距离信息


    private void OnDrawGizmos()
    {
        int k = 0;
        for (float x = 0; x < 1f; x += xPrecise)
        {
            for (float y = 0; y < 1f; y += yPrecise)
            {
                float rEnhance = k >= r.Length ? 1f : r[k];
                Vector3 point3D = EqMapToDir(new Vector2(x, y)) * radius * rEnhance;
                Gizmos.DrawLine(point3D, point3D + point3D.normalized * 0.1f);
                k++;
            }
        }
    }

    private Vector3 EqMapToDir(Vector2 uv)
    {
        float v = 1f - uv.y;
        float theta = v * Mathf.PI;

        float u = uv.x;
        float phi = u * 2f * Mathf.PI;

        float x = Mathf.Sin(phi) * Mathf.Sin(theta) * -1;
        float y = Mathf.Cos(theta);
        float z = Mathf.Cos(phi) * Mathf.Sin(theta) * -1;

        return new Vector3(x, y, z);
    }

    [ContextMenu("Calc R")]
    private void CalcR()//烘焙距离
    {
        r = new float[Mathf.CeilToInt(1f / yPrecise) * Mathf.CeilToInt(1f / xPrecise)];

        int k = 0;
        for (float x = 0; x < 1f; x += xPrecise)
        {
            for (float y = 0; y < 1f; y += yPrecise)
            {
                Vector3 point3D = EqMapToDir(new Vector2(x, y)) * radius;

                if (Physics.Raycast(point3D, -point3D.normalized, out RaycastHit hit))
                {
                    r[k] = hit.point.magnitude / radius;//这里的计算其实不正确
                }

                k++;
            }
        }
    }

    [ContextMenu("Bake Texture")]
    private void BakeTexture()//烘焙成全景图
    {
        int width = Mathf.CeilToInt(1f / xPrecise);
        int height = Mathf.CeilToInt(1f / yPrecise);
        Texture2D tex = new Texture2D(width, height, TextureFormat.ARGB32, true);

        int k = 0;
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                float rEnhance = k >= r.Length ? 1f : r[k];
                tex.SetPixel(x, y, new Color(rEnhance, rEnhance, rEnhance, 1f));
                k++;
            }
        }

        tex.Apply();

#if UNITY_EDITOR
        System.IO.File.WriteAllBytes(System.IO.Directory.GetCurrentDirectory() + "/Assets/AOField.png", tex.EncodeToPNG());
        UnityEditor.AssetDatabase.Refresh();
#endif
    }
}
AOFields.cs

 

传入测试平面位置数据:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AOFieldsTestPlane : MonoBehaviour
{
    public MeshRenderer meshRenderer;
    public Transform obstacleTransform;


    private void Update()
    {
        meshRenderer.sharedMaterial.SetVector("_ObstaclePos", obstacleTransform.position);
    }
}
AOFieldsTestPlane.cs

 

Shader部分:

Shader "Unlit/AOFields"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _AOFieldTex("AO Field Tex", 2D) = "white"{}
        _AOFieldScale("AO Field Scale",float) = 1.0//缩放
        _AOLimitValue("AO Limit Value", float) = 0.5//最大AO限制
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"
        }
        
        CULL OFF

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 wPos : TEXCOORD1;
                float3 wNormal : TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _AOFieldTex;
            float _AOFieldScale;

            float _AOLimitValue;

            uniform float3 _ObstaclePos;

            
            float2 DirToEqMap(float3 a_coords)
            {
                float3 a_coords_n = normalize(a_coords);
                float lon = atan2(a_coords_n.z, a_coords_n.x);
                float lat = acos(a_coords_n.y);
                float2 sphereCoords = float2(lon, lat) * (1.0 / UNITY_PI);
                return float2( (sphereCoords.x * 0.5 + 0.5), 1 - sphereCoords.y);
            }

            //一个简单粗暴的AO实现
            float Occlusion(float3 pos, float3 nor, float3 sph)
            {
                float3 di = pos - sph;
                float3 diNom = normalize(di);
                float2 uv = DirToEqMap(diNom);
                float intensity = tex2D(_AOFieldTex, uv).r;
                float3 cPoint = sph + diNom * intensity * _AOFieldScale;//得到球体内模型的具体距离点
                return smoothstep(0.0, 1.0, distance(pos,cPoint) / _AOLimitValue);
            }

            v2f vert(appdata v)
            {
                v2f o = (v2f)0;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.wPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.wNormal = mul((float3x3)UNITY_MATRIX_M, v.normal);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                float occ0 = Occlusion(i.wPos, i.wNormal, _ObstaclePos);
                return float4(occ0,occ0,occ0, 1.0);
            }
            ENDCG
        }
    }
}
AOFields.shader

 

标签:uv,球面,float,Mathf,AO,简易,1f,public,float3
来源: https://www.cnblogs.com/hont/p/15224873.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有