在 Shaderlab 中初始化全局变量的问题
Issue with initializing global variable in Shaderlab
我刚刚意识到Unity中的全局变量必须在函数中初始化。作为着色器的新手,我不知道我是否以错误的方式这样做。
比如我定义一个const变量时:
const float PI = 3.14159265359;
然后尝试编写一个代码,在 frag 函数中使用:
fixed4 frag(v2f i) : SV_Target
{
float result = PI * otherVariable;
///Use the result value...
}
没用。结果出现黑屏,或者预期的结果不会出现。
令人困惑的是,当我在 frag 函数中再次 重新初始化 全局变量 (PI) 时,我得到了预期的结果。
例如,这是有效的:
float PI = 3.14159265359;
然后试着写一段代码在frag函数中使用:
fixed4 frag(v2f i) : SV_Target
{
//re-initialize
PI = 3.14159265359;
float result = PI * otherVariable;
}
我有太多常量全局变量,以至于我无法重新启动它们。
此问题的一个完整示例是 this 我移植的 Shadertoy 代码。
Shader "Unlit/Atmospheric Scattering 2"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
//https://www.shadertoy.com/view/lslXDr
// math const
float PI = 3.14159265359;
float DEG_TO_RAD = (3.14159265359 / 180.0);
float MAX = 10000.0;
// scatter const
float K_R = 0.166;
float K_M = 0.0025;
float E = 14.3;
float C_R = float3(0.3, 0.7, 1.0); // 1 / wavelength ^ 4
float G_M = -0.85; // Mie g
float R = 1.0;
float R_INNER = 0.7;
float SCALE_H = 4.0 / (1.0 - 0.7);
float SCALE_L = 1.0 / (1.0 - 0.7);
int NUM_OUT_SCATTER = 10;
float FNUM_OUT_SCATTER = 10.0;
int NUM_IN_SCATTER = 10;
float FNUM_IN_SCATTER = 10.0;
// angle : pitch, yaw
float3x3 rot3xy(float2 angle) {
float2 c = cos(angle);
float2 s = sin(angle);
return float3x3(
c.y, 0.0, -s.y,
s.y * s.x, c.x, c.y * s.x,
s.y * c.x, -s.x, c.y * c.x
);
}
// ray direction
float3 ray_dir(float fov, float2 size, float2 pos) {
float2 xy = pos - size * 0.5;
float cot_half_fov = tan((90.0 - fov * 0.5) * DEG_TO_RAD);
float z = size.y * 0.5 * cot_half_fov;
return normalize(float3(xy, -z));
}
// ray intersects sphere
// e = -b +/- sqrt( b^2 - c )
float2 ray_vs_sphere(float3 p, float3 dir, float r) {
float b = dot(p, dir);
float c = dot(p, p) - r * r;
float d = b * b - c;
if (d < 0.0) {
return float2(MAX, -MAX);
}
d = sqrt(d);
return float2(-b - d, -b + d);
}
// Mie
// g : ( -0.75, -0.999 )
// 3 * ( 1 - g^2 ) 1 + c^2
// F = ----------------- * -------------------------------
// 2 * ( 2 + g^2 ) ( 1 + g^2 - 2 * g * c )^(3/2)
float phase_mie(float g, float c, float cc) {
float gg = g * g;
float a = (1.0 - gg) * (1.0 + cc);
float b = 1.0 + gg - 2.0 * g * c;
b *= sqrt(b);
b *= 2.0 + gg;
//b = mul(b,sqrt(b));
//b = mul(b,2.0 + gg);
return 1.5 * a / b;
}
// Reyleigh
// g : 0
// F = 3/4 * ( 1 + c^2 )
float phase_reyleigh(float cc) {
return 0.75 * (1.0 + cc);
}
float density(float3 p) {
return exp(-(length(p) - R_INNER) * SCALE_H);
}
float optic(float3 p, float3 q) {
float3 step = (q - p) / FNUM_OUT_SCATTER;
float3 v = p + step * 0.5;
float sum = 0.0;
for (int i = 0; i < NUM_OUT_SCATTER; i++) {
sum += density(v);
v += step;
}
sum *= length(step) * SCALE_L;
//sum = mul(sum,length(step) * SCALE_L);
return sum;
}
float3 in_scatter(float3 o, float3 dir, float2 e, float3 l) {
float len = (e.y - e.x) / FNUM_IN_SCATTER;
float3 step = dir * len;
float3 p = o + dir * e.x;
float3 v = p + dir * (len * 0.5);
float3 sum = float3(0.,0.,0.);
for (int i = 0; i < NUM_IN_SCATTER; i++) {
float2 f = ray_vs_sphere(v, l, R);
float3 u = v + l * f.y;
float n = (optic(p, v) + optic(v, u)) * (PI * 4.0);
sum += density(v) * exp(-n * (K_R * C_R + K_M));
v += step;
}
sum *= len * SCALE_L;
//sum = mul(sum,len * SCALE_L);
float c = dot(dir, -l);
float cc = c * c;
return sum * (K_R * C_R * phase_reyleigh(cc) + K_M * phase_mie(G_M, c, cc)) * E;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
fixed4 frag(v2f i) : SV_Target
{
//Re-initialize BEGIN
// math const
PI = 3.14159265359;
DEG_TO_RAD = (3.14159265359 / 180.0);
MAX = 10000.0;
// scatter const
K_R = 0.166;
K_M = 0.0025;
E = 14.3;
C_R = float3(0.3, 0.7, 1.0); // 1 / wavelength ^ 4
G_M = -0.85; // Mie g
R = 1.0;
R_INNER = 0.7;
SCALE_H = 4.0 / (1.0 - 0.7);
SCALE_L = 1.0 / (1.0 - 0.7);
NUM_OUT_SCATTER = 10;
FNUM_OUT_SCATTER = 10.0;
NUM_IN_SCATTER = 10;
FNUM_IN_SCATTER = 10.0;
//Re-initialize END
float4 fragColor = 0;
float2 fragCoord = i.vertex.xy;
// default ray dir
float3 dir = ray_dir(45.0, _ScreenParams.xy, fragCoord.xy);
// default ray origin
float3 eye = float3(0.0, 0.0, 2.4);
// rotate camera
float3x3 rot = rot3xy(float2(0.0, _Time.y * 0.5));
/* dir = rot * dir;
eye = rot * eye;*/
dir = mul(rot,dir);
eye = mul(rot,eye);
// sun light dir
float3 l = float3(0, 0, 1);
float2 e = ray_vs_sphere(eye, dir, R);
if (e.x > e.y) {
discard;
}
float2 f = ray_vs_sphere(eye, dir, R_INNER);
e.y = min(e.y, f.x);
float3 I = in_scatter(eye, dir, e, l);
fragColor = float4(I, 1.0);
return fragColor;
}
ENDCG
}
}
}
此着色器附加到 Material,然后使用以下代码附加到相机:
[ExecuteInEditMode]
public class CameraEffect : MonoBehaviour
{
public Material mat;
// Called by camera to apply image effect
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (mat != null)
{
Graphics.Blit(source, destination, mat);
}
else
{
Graphics.Blit(source, destination);
}
}
}
它工作正常,但请注意我必须如何重新初始化 PI
、DEG_TO_RAD
、MAX
和其他全局变量...如果不这样做,它不会 work.The 屏幕只是黑色,这不是第一个导致同样问题的着色器。
为什么会这样?
我是否以错误的方式声明了变量?
嗨!
任何常量都可以这样写:
//float my_constant = 104.3;
#define my_constant 104.3f
您可以为着色器创建自定义库,并根据需要使用变量和函数。例如,创建名称为 MyConstants.cginc
的文件并放入您的项目中。代码:
#ifndef MY_CONSTANTS_INCLUDED
#define MY_CONSTANTS_INCLUDED
#define DEG_TO_RAD 0.01745329251994f
#define MAX 10000.0f
#define PI 3.14159265359f
//scatter const
// .....
// .....
float3 ray_dir(float fov, float2 size, float2 pos) {
float2 xy = pos - size * 0.5;
float cot_half_fov = tan((90.0 - fov * 0.5) * DEG_TO_RAD);
float z = size.y * 0.5 * cot_half_fov;
return normalize(float3(xy, -z));
}
//.... and other methods
#endif
并在着色器中使用库
//.....
#include "UnityCG.cginc"
#include "MyConstants.cginc"
//.....
我刚刚意识到Unity中的全局变量必须在函数中初始化。作为着色器的新手,我不知道我是否以错误的方式这样做。
比如我定义一个const变量时:
const float PI = 3.14159265359;
然后尝试编写一个代码,在 frag 函数中使用:
fixed4 frag(v2f i) : SV_Target
{
float result = PI * otherVariable;
///Use the result value...
}
没用。结果出现黑屏,或者预期的结果不会出现。
令人困惑的是,当我在 frag 函数中再次 重新初始化 全局变量 (PI) 时,我得到了预期的结果。
例如,这是有效的:
float PI = 3.14159265359;
然后试着写一段代码在frag函数中使用:
fixed4 frag(v2f i) : SV_Target
{
//re-initialize
PI = 3.14159265359;
float result = PI * otherVariable;
}
我有太多常量全局变量,以至于我无法重新启动它们。
此问题的一个完整示例是 this 我移植的 Shadertoy 代码。
Shader "Unlit/Atmospheric Scattering 2"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
//https://www.shadertoy.com/view/lslXDr
// math const
float PI = 3.14159265359;
float DEG_TO_RAD = (3.14159265359 / 180.0);
float MAX = 10000.0;
// scatter const
float K_R = 0.166;
float K_M = 0.0025;
float E = 14.3;
float C_R = float3(0.3, 0.7, 1.0); // 1 / wavelength ^ 4
float G_M = -0.85; // Mie g
float R = 1.0;
float R_INNER = 0.7;
float SCALE_H = 4.0 / (1.0 - 0.7);
float SCALE_L = 1.0 / (1.0 - 0.7);
int NUM_OUT_SCATTER = 10;
float FNUM_OUT_SCATTER = 10.0;
int NUM_IN_SCATTER = 10;
float FNUM_IN_SCATTER = 10.0;
// angle : pitch, yaw
float3x3 rot3xy(float2 angle) {
float2 c = cos(angle);
float2 s = sin(angle);
return float3x3(
c.y, 0.0, -s.y,
s.y * s.x, c.x, c.y * s.x,
s.y * c.x, -s.x, c.y * c.x
);
}
// ray direction
float3 ray_dir(float fov, float2 size, float2 pos) {
float2 xy = pos - size * 0.5;
float cot_half_fov = tan((90.0 - fov * 0.5) * DEG_TO_RAD);
float z = size.y * 0.5 * cot_half_fov;
return normalize(float3(xy, -z));
}
// ray intersects sphere
// e = -b +/- sqrt( b^2 - c )
float2 ray_vs_sphere(float3 p, float3 dir, float r) {
float b = dot(p, dir);
float c = dot(p, p) - r * r;
float d = b * b - c;
if (d < 0.0) {
return float2(MAX, -MAX);
}
d = sqrt(d);
return float2(-b - d, -b + d);
}
// Mie
// g : ( -0.75, -0.999 )
// 3 * ( 1 - g^2 ) 1 + c^2
// F = ----------------- * -------------------------------
// 2 * ( 2 + g^2 ) ( 1 + g^2 - 2 * g * c )^(3/2)
float phase_mie(float g, float c, float cc) {
float gg = g * g;
float a = (1.0 - gg) * (1.0 + cc);
float b = 1.0 + gg - 2.0 * g * c;
b *= sqrt(b);
b *= 2.0 + gg;
//b = mul(b,sqrt(b));
//b = mul(b,2.0 + gg);
return 1.5 * a / b;
}
// Reyleigh
// g : 0
// F = 3/4 * ( 1 + c^2 )
float phase_reyleigh(float cc) {
return 0.75 * (1.0 + cc);
}
float density(float3 p) {
return exp(-(length(p) - R_INNER) * SCALE_H);
}
float optic(float3 p, float3 q) {
float3 step = (q - p) / FNUM_OUT_SCATTER;
float3 v = p + step * 0.5;
float sum = 0.0;
for (int i = 0; i < NUM_OUT_SCATTER; i++) {
sum += density(v);
v += step;
}
sum *= length(step) * SCALE_L;
//sum = mul(sum,length(step) * SCALE_L);
return sum;
}
float3 in_scatter(float3 o, float3 dir, float2 e, float3 l) {
float len = (e.y - e.x) / FNUM_IN_SCATTER;
float3 step = dir * len;
float3 p = o + dir * e.x;
float3 v = p + dir * (len * 0.5);
float3 sum = float3(0.,0.,0.);
for (int i = 0; i < NUM_IN_SCATTER; i++) {
float2 f = ray_vs_sphere(v, l, R);
float3 u = v + l * f.y;
float n = (optic(p, v) + optic(v, u)) * (PI * 4.0);
sum += density(v) * exp(-n * (K_R * C_R + K_M));
v += step;
}
sum *= len * SCALE_L;
//sum = mul(sum,len * SCALE_L);
float c = dot(dir, -l);
float cc = c * c;
return sum * (K_R * C_R * phase_reyleigh(cc) + K_M * phase_mie(G_M, c, cc)) * E;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
fixed4 frag(v2f i) : SV_Target
{
//Re-initialize BEGIN
// math const
PI = 3.14159265359;
DEG_TO_RAD = (3.14159265359 / 180.0);
MAX = 10000.0;
// scatter const
K_R = 0.166;
K_M = 0.0025;
E = 14.3;
C_R = float3(0.3, 0.7, 1.0); // 1 / wavelength ^ 4
G_M = -0.85; // Mie g
R = 1.0;
R_INNER = 0.7;
SCALE_H = 4.0 / (1.0 - 0.7);
SCALE_L = 1.0 / (1.0 - 0.7);
NUM_OUT_SCATTER = 10;
FNUM_OUT_SCATTER = 10.0;
NUM_IN_SCATTER = 10;
FNUM_IN_SCATTER = 10.0;
//Re-initialize END
float4 fragColor = 0;
float2 fragCoord = i.vertex.xy;
// default ray dir
float3 dir = ray_dir(45.0, _ScreenParams.xy, fragCoord.xy);
// default ray origin
float3 eye = float3(0.0, 0.0, 2.4);
// rotate camera
float3x3 rot = rot3xy(float2(0.0, _Time.y * 0.5));
/* dir = rot * dir;
eye = rot * eye;*/
dir = mul(rot,dir);
eye = mul(rot,eye);
// sun light dir
float3 l = float3(0, 0, 1);
float2 e = ray_vs_sphere(eye, dir, R);
if (e.x > e.y) {
discard;
}
float2 f = ray_vs_sphere(eye, dir, R_INNER);
e.y = min(e.y, f.x);
float3 I = in_scatter(eye, dir, e, l);
fragColor = float4(I, 1.0);
return fragColor;
}
ENDCG
}
}
}
此着色器附加到 Material,然后使用以下代码附加到相机:
[ExecuteInEditMode]
public class CameraEffect : MonoBehaviour
{
public Material mat;
// Called by camera to apply image effect
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (mat != null)
{
Graphics.Blit(source, destination, mat);
}
else
{
Graphics.Blit(source, destination);
}
}
}
它工作正常,但请注意我必须如何重新初始化 PI
、DEG_TO_RAD
、MAX
和其他全局变量...如果不这样做,它不会 work.The 屏幕只是黑色,这不是第一个导致同样问题的着色器。
为什么会这样?
我是否以错误的方式声明了变量?
嗨!
任何常量都可以这样写:
//float my_constant = 104.3;
#define my_constant 104.3f
您可以为着色器创建自定义库,并根据需要使用变量和函数。例如,创建名称为 MyConstants.cginc
的文件并放入您的项目中。代码:
#ifndef MY_CONSTANTS_INCLUDED
#define MY_CONSTANTS_INCLUDED
#define DEG_TO_RAD 0.01745329251994f
#define MAX 10000.0f
#define PI 3.14159265359f
//scatter const
// .....
// .....
float3 ray_dir(float fov, float2 size, float2 pos) {
float2 xy = pos - size * 0.5;
float cot_half_fov = tan((90.0 - fov * 0.5) * DEG_TO_RAD);
float z = size.y * 0.5 * cot_half_fov;
return normalize(float3(xy, -z));
}
//.... and other methods
#endif
并在着色器中使用库
//.....
#include "UnityCG.cginc"
#include "MyConstants.cginc"
//.....