Tôi hiện đang cố gắng chuyển vùng đổ bóng shadertoy.com (Atmospheric Scattering Sample, bản trình diễn tương tác với mã) thành Unity. Trình đổ bóng được viết bằng GLSL và tôi phải khởi động trình chỉnh sửa với C:\Program Files\Unity\Editor>Unity.exe -force-opengl
để làm cho trình đổ bóng hiển thị (nếu không, lỗi "Trình đổ bóng này không thể chạy trên GPU này"), nhưng đó không phải là vấn đề ngay bây giờ. Vấn đề là chuyển porter đó sang Unity.Sự cố khi chuyển đổ bóng đổ bóng GLSL sang thống nhất
Các chức năng tán xạ vv đều giống nhau và "runnable" trong trình đổ bóng được chuyển của tôi, điều duy nhất là chức năng mainImage()
quản lý máy ảnh, hướng ánh sáng và hướng tia. Điều này đã được ofcourse thay đổi vị trí máy ảnh của Unity Unity, hướng nhìn và nguồn ánh sáng và hướng dẫn được sử dụng.
Chức năng chính của cái nhìn ban đầu như thế này:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
// default ray dir
vec3 dir = ray_dir(45.0, iResolution.xy, fragCoord.xy);
// default ray origin
vec3 eye = vec3(0.0, 0.0, 2.4);
// rotate camera
mat3 rot = rot3xy(vec2(0.0, iGlobalTime * 0.5));
dir = rot * dir;
eye = rot * eye;
// sun light dir
vec3 l = vec3(0, 0, 1);
vec2 e = ray_vs_sphere(eye, dir, R);
if (e.x > e.y) {
discard;
}
vec2 f = ray_vs_sphere(eye, dir, R_INNER);
e.y = min(e.y, f.x);
vec3 I = in_scatter(eye, dir, e, l);
fragColor = vec4(I, 1.0);
}
Tôi đã đọc qua tài liệu của chức năng đó và làm thế nào đó là nghĩa vụ làm việc tại https://www.shadertoy.com/howto.
Trình tạo bóng hình ảnh triển khai hàm mainImage() để tạo ra hình ảnh thủ tục bằng cách tính màu cho mỗi pixel. Chức năng này được dự kiến sẽ được gọi một lần cho mỗi pixel, và nó là khả năng đáp ứng của ứng dụng máy chủ để cung cấp đầu vào phù hợp với và nhận màu đầu ra từ đó và gán nó cho pixel màn hình. Nguyên mẫu là:
void mainImage (ra vec4 fragMàu, trong vec2 fragCoord);
trong đó fragCoord chứa toạ độ pixel mà bóng đổ cần tính màu. Các tọa độ nằm trong các đơn vị pixel, khác nhau, từ 0,5 đến độ phân giải 0,5, trên bề mặt hiển thị, ở đó độ phân giải được chuyển đến trình đổ bóng thông qua đồng phục iResolution (xem bên dưới).
Màu kết quả được thu thập bằng màu sắc dưới dạng bốn thành phần vectơ, phần cuối cùng trong số đó bị máy khách bỏ qua. Kết quả là được thu thập dưới dạng biến "ngoài" trong hình ảnh của việc bổ sung nhiều mục tiêu kết xuất sau này.
Vì vậy, trong hàm đó, có tham chiếu đến iGlobalTime
để làm cho máy ảnh xoay theo thời gian và tham chiếu đến độ phân giải iResolution
. Tôi đã nhúng trình đổ bóng vào bộ đổ bóng Unity và cố gắng sửa chữa và kết nối dây chuyền dir
, eye
và l
để nó hoạt động với Unity, nhưng tôi hoàn toàn bị kẹt. Tôi nhận được một số loại hình trông "có liên quan" đến đổ bóng ban đầu: (Top là bản gốc, buttom trạng thái thống nhất hiện nay)
Tôi không phải là một chuyên nghiệp đổ bóng, tôi chỉ biết một số điều cơ bản của OpenGL, nhưng phần lớn, tôi viết logic trò chơi trong C#, vì vậy tất cả những gì tôi có thể làm là xem xét các ví dụ khác về bóng và xem cách tôi có thể lấy dữ liệu về máy ảnh, nguồn sáng vv trong mã này, nhưng bạn có thể thấy, không có gì hoạt động, thực sự.
Tôi đã sao chép mã skelton cho trình đổ bóng từ https://en.wikibooks.org/wiki/GLSL_Programming/Unity/Specular_Highlights và một số vectơ từ http://forum.unity3d.com/threads/glsl-shader.39629/.
Tôi hy vọng một người nào đó có thể chỉ cho tôi một số hướng về cách khắc phục trình đổ bóng này/chính xác chuyển nó thành sự hiệp nhất. Dưới đây là mã shader hiện tại, tất cả những gì bạn phải làm để tạo lại nó là tạo một shader mới trong một dự án trống, sao chép mã bên trong, tạo một tài liệu mới, gán shader cho tài liệu đó, sau đó thêm một hình cầu và thêm tài liệu đó trên đó và thêm ánh sáng hướng.
Shader "Unlit/AtmoFragShader" {
Properties{
_MainTex("Base (RGB)", 2D) = "white" {}
_LC("LC", Color) = (1,0,0,0) /* stuff from the testing shader, now really used */
_LP("LP", Vector) = (1,1,1,1)
}
SubShader{
Tags{ "Queue" = "Geometry" } //Is this even the right queue?
Pass{
//Tags{ "LightMode" = "ForwardBase" }
GLSLPROGRAM
/* begin port by copying in the constants */
// math const
const float PI = 3.14159265359;
const float DEG_TO_RAD = PI/180.0;
const float MAX = 10000.0;
// scatter const
const float K_R = 0.166;
const float K_M = 0.0025;
const float E = 14.3; // light intensity
const vec3 C_R = vec3(0.3, 0.7, 1.0); // 1/wavelength^4
const float G_M = -0.85; // Mie g
const float R = 1.0; /* this is the radius of the spehere? this should be set from the geometry or something.. */
const float R_INNER = 0.7;
const float SCALE_H = 4.0/(R - R_INNER);
const float SCALE_L = 1.0/(R - R_INNER);
const int NUM_OUT_SCATTER = 10;
const float FNUM_OUT_SCATTER = 10.0;
const int NUM_IN_SCATTER = 10;
const float FNUM_IN_SCATTER = 10.0;
/* begin functions. These are out of the defines because they should be accesible to anyone. */
// angle : pitch, yaw
mat3 rot3xy(vec2 angle) {
vec2 c = cos(angle);
vec2 s = sin(angle);
return mat3(
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
vec3 ray_dir(float fov, vec2 size, vec2 pos) {
vec2 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(vec3(xy, -z));
}
// ray intersects sphere
// e = -b +/- sqrt(b^2 - c)
vec2 ray_vs_sphere(vec3 p, vec3 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 vec2(MAX, -MAX);
}
d = sqrt(d);
return vec2(-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;
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(vec3 p) {
return exp(-(length(p) - R_INNER) * SCALE_H);
}
float optic(vec3 p, vec3 q) {
vec3 step = (q - p)/FNUM_OUT_SCATTER;
vec3 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;
return sum;
}
vec3 in_scatter(vec3 o, vec3 dir, vec2 e, vec3 l) {
float len = (e.y - e.x)/FNUM_IN_SCATTER;
vec3 step = dir * len;
vec3 p = o + dir * e.x;
vec3 v = p + dir * (len * 0.5);
vec3 sum = vec3(0.0);
for (int i = 0; i < NUM_IN_SCATTER; i++) {
vec2 f = ray_vs_sphere(v, l, R);
vec3 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;
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;
}
/* end functions */
/* vertex shader begins here*/
#ifdef VERTEX
const float SpecularContribution = 0.3;
const float DiffuseContribution = 1.0 - SpecularContribution;
uniform vec4 _LP;
varying vec2 TextureCoordinate;
varying float LightIntensity;
varying vec4 someOutput;
/* transient stuff */
varying vec3 eyeOutput;
varying vec3 dirOutput;
varying vec3 lOutput;
varying vec2 eOutput;
/* lighting stuff */
// i.e. one could #include "UnityCG.glslinc"
uniform vec3 _WorldSpaceCameraPos;
// camera position in world space
uniform mat4 _Object2World; // model matrix
uniform mat4 _World2Object; // inverse model matrix
uniform vec4 _WorldSpaceLightPos0;
// direction to or position of light source
uniform vec4 _LightColor0;
// color of light source (from "Lighting.cginc")
void main()
{
/* code from that example shader */
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Vertex);
vec3 tnorm = normalize(gl_NormalMatrix * gl_Normal);
vec3 lightVec = normalize(_LP.xyz - ecPosition);
vec3 reflectVec = reflect(-lightVec, tnorm);
vec3 viewVec = normalize(-ecPosition);
/* copied from https://en.wikibooks.org/wiki/GLSL_Programming/Unity/Specular_Highlights for testing stuff */
//I have no idea what I'm doing, but hopefully this computes some vectors which I need
mat4 modelMatrix = _Object2World;
mat4 modelMatrixInverse = _World2Object; // unity_Scale.w
// is unnecessary because we normalize vectors
vec3 normalDirection = normalize(vec3(
vec4(gl_Normal, 0.0) * modelMatrixInverse));
vec3 viewDirection = normalize(vec3(
vec4(_WorldSpaceCameraPos, 1.0)
- modelMatrix * gl_Vertex));
vec3 lightDirection;
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
attenuation = 1.0; // no attenuation
lightDirection = normalize(vec3(_WorldSpaceLightPos0));
}
else // point or spot light
{
vec3 vertexToLightSource = vec3(_WorldSpaceLightPos0
- modelMatrix * gl_Vertex);
float distance = length(vertexToLightSource);
attenuation = 1.0/distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}
/* test port */
// default ray dir
//That's the direction of the camera here?
vec3 dir = viewDirection; //normalDirection;//viewDirection;// tnorm;//lightVec;//lightDirection;//normalDirection; //lightVec;//tnorm;//ray_dir(45.0, iResolution.xy, fragCoord.xy);
// default ray origin
//I think they mean the position of the camera here?
vec3 eye = vec3(_WorldSpaceCameraPos); //vec3(_WorldSpaceLightPos0); //// vec3(0.0, 0.0, 0.0); //_WorldSpaceCameraPos;//ecPosition; //vec3(0.0, 0.0, 2.4);
// rotate camera not needed, remove it
// sun light dir
//I think they mean the direciton of our directional light?
vec3 l = lightDirection;//_LightColor0.xyz; //lightDirection; //normalDirection;//normalize(vec3(_WorldSpaceLightPos0));//lightVec;// vec3(0, 0, 1);
/* this computes the intersection of the ray and the sphere.. is this really needed?*/
vec2 e = ray_vs_sphere(eye, dir, R);
/* copy stuff sothat we can use it on the fragment shader, "discard" is only allowed in fragment shader,
so the rest has to be computed in fragment shader */
eOutput = e;
eyeOutput = eye;
dirOutput = dir;
lOutput = dir;
}
#endif
#ifdef FRAGMENT
uniform sampler2D _MainTex;
varying vec2 TextureCoordinate;
uniform vec4 _LC;
varying float LightIntensity;
/* transient port */
varying vec3 eyeOutput;
varying vec3 dirOutput;
varying vec3 lOutput;
varying vec2 eOutput;
void main()
{
/* real fragment */
if (eOutput.x > eOutput.y) {
//discard;
}
vec2 f = ray_vs_sphere(eyeOutput, dirOutput, R_INNER);
vec2 e = eOutput;
e.y = min(e.y, f.x);
vec3 I = in_scatter(eyeOutput, dirOutput, eOutput, lOutput);
gl_FragColor = vec4(I, 1.0);
/*vec4 c2;
c2.x = 1.0;
c2.y = 1.0;
c2.z = 0.0;
c2.w = 1.0f;
gl_FragColor = c2;*/
//gl_FragColor = c;
}
#endif
ENDGLSL
}
}
}
Bất kỳ trợ giúp nào được đánh giá cao, xin lỗi về bài đăng và giải thích dài.
Chỉnh sửa: Tôi vừa phát hiện ra rằng bán kính của quả cầu không có ảnh hưởng đến đồ vật, quả cầu có quy mô 2.0 theo mọi hướng cho kết quả tốt hơn nhiều. Tuy nhiên, hình ảnh vẫn hoàn toàn độc lập với góc nhìn của máy ảnh và bất kỳ đèn nào, đây là hư không gần phiên bản shaderlab.
câu hỏi hay ... – Fattie
@OP Tôi chỉ cần nhảy vào trình đổ bóng. Bạn đã kết thúc giải quyết vấn đề này? – Programmer
@Programmer Đã không làm việc trên nó bao giờ hết - tất cả mọi người có thể cảm thấy tự do để thử và thực hiện các đề xuất trong câu trả lời. –