入门图形学:VR畸变后处理

本文讲述了在Unity中处理VR画面畸变的问题,通过调整和优化Shader实现桶形畸变校正。作者首先介绍了现有算法,然后针对_Distort负值时出现的问题进行修正,确保四个UV端点变换后保持不变,并提供了C#代码实现参数动态调整。最终实现了用户可配置的‘凹凸’参数,提升了VR体验。
摘要由CSDN通过智能技术生成

      最近真的忙,项目需求大改,晚上还要复习成人英语考试。
      新增了“一键生成VR”需求,在现有框架上预编译同步普通键鼠和VR手柄的操作、同步PC和VR的各种接口使用等,业务开发人员通过派生、调用新的基类和API做流程功能,然后使用编辑器功能一键生成VR和PC双端场景并完整运行,同时业务功能也是大改,耳边还回响部门领导对我们项目经理喷的“都是错的!做的都是错的!!!”。
      作为大头兵也没办法,只能按照新要求来改,就是做无用功浪费的时间回不来了。
      现在VR上有个需求就是画面畸变,这VR头盔真是一家一个样,就算同一个头盔不同的人用都说看到的效果千奇百怪,特别是空间感上的差别,于是需要一个方法解决这种问题。我准备使用画面后处理畸变解决。
      看看传统画面畸变的方法:桶形畸变
      百度 桶形畸变
      wiki 畸变
      纹理uv网格上看,像个那种存酒的木桶一样,就类似这种:
在这里插入图片描述      图片看得出来,uv从中心(0.5,0.5)到四端有一种“膨胀”的感觉,我们可以猜想想到是否有一种计算方法可以将uv变换成这样。我网上找了个算法再结合了一点自己的修改,如下:
      首先是网上的算法:

Shader "VRDistort/VRDistortImageEffectShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Distort("Distort",Range(-4,4)) = 0
        _Adjust("Adjust",Range(0,4)) = 1
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            float _Distort;             //畸变系数
            float _Adjust;              //矫正系数

            //桶形畸变
            float2 barreldistort(float2 uv)
            {
                //得到pixeluv相对center坐标d
                float2 d = uv.xy-float2(0.5,0.5);
                //d的长度方(0<d<0.5)
                float2 d2 = d.x*d.x+d.y*d.y;
                //畸变权重(-1<f<3)
                float f = 1.0+d2*_Distort;
                //畸变uv
                //(f*_Adjust)用于修正权重
                //再*d后“还原”相对坐标d
                //再+center还原pixeluv
                float2 buv = f*_Adjust*d+float2(0.5,0.5);
                return buv;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, barreldistort(i.uv));
                return col;
            }
            ENDCG
        }
    }
}

      这个算法设计很巧妙,利用pixeluv与centeruv相对坐标d,进行变换后还原到pixeluv,再调整畸变参数和修正参数,就可以得到桶形畸变的效果,如下:
在这里插入图片描述
      这里使用OnRenderImage进行后处理画面,可以看的出来畸变效果ok。
      同时会发现一个问题:uv区间[0,1]的改变,畸变后需要手动调整修正参数才能将纹理“四个角”再次匹配。
      所以我们还要修改一下算法,去掉_Adjust参数,我们的算法要保证:
      1.自动计算_Adjust参数
      2.4个uv端点变换后uv依旧相同
0
      上面的计算就是将P0(0,0)、P1(1,1)带入原BarrelDistort算法,就可以得到_Adjust = 1/(1+0.5*_Distort)。
      修改代码:

float2 barrel(float2 uv)
{
    float j = 1/(1+0.5*_Distort);
    float2 d = uv.xy-float2(0.5,0.5);
    float d2 = d.x*d.x+d.y*d.y;
    float f = 1.0+d2*_Distort;
    float2 buv = f*j*d+float2(0.5,0.5);
    return buv;
}

      效果如下:
在这里插入图片描述
      可以看的出来算法在_Distort>0的区间内没问题,但是_Distort<0的区间内就出问题了,问题如下:
      1.画面变“凹”后,屏幕四边“凹陷”的uv不能“补全”。
      2.当_Distort=-2时,分母=0,则计算错误。
      那么我们还要思考_Distort<0时,正确的uv映射关系,如下:
在这里插入图片描述
      四边中心点pixeluv经过变换后还是原pixeluv,计算如下:
在这里插入图片描述
      计算方法就是反推,简单的一元一次方程。
      修改代码:

float2 barrel(float2 uv)
{
    float j = _Distort >= 0 ? (1/(1+0.5*_Distort)) : (1/(1+0.25*_Distort));
    float2 d = uv.xy-float2(0.5,0.5);
    float d2 = d.x*d.x+d.y*d.y;
    float f = 1.0+d2*_Distort;
    float2 buv = f*j*d+float2(0.5,0.5);
    return buv;
}

      效果如下:
在这里插入图片描述
      这样就达到效果了,用户设置界面就只需要给一个“凹凸”参数微调画面就行了。
      当然建议_Distort和_Adjust由c#计算后传递,节省shader运算时间,如下:

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

public class VRDistortSetting : MonoBehaviour
{
    public Slider slider;

    public Material mat;

    void Start()
    {
        slider.onValueChanged.AddListener(SliderValueChange);
    }

    private void SliderValueChange(float val)
    {
        float distort = val;
        float adjust = distort >= 0 ? (1 / (1 + 0.5f * distort)) : (1 / (1 + 0.25f * distort));
        mat.SetFloat("_Distort", distort);
        mat.SetFloat("_Adjust", adjust );
    }
}

      ok,今天到这里,估计只有五一能闲一下了。

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值