realtime sss demo movie

Started by
3 comments, last by rouncED 14 years ago
">REALTIME SSS VID heres an sss program I put together, it looks 10 million times better without the stupid video I screen grabbed for it, god it turned out so useless, but check it out tell me what you think. As for details, its a gpu based algorythm, it looks ok for marble or gem like material but for skin its not quite working as it is, maybe its something to do with specular or maybe its not transmitting perfectly, not sure. I grab a circle of samples for every pixel containing the model and grab all the other pixels of close distance and merge them weighted by their worldspace distance apart I read with further sampling a xyz viewspace render, I do this for the front and the back of the model. If it was used in a game, it takes a single pass of the screen to do the whole lot, as it is I am running it at half res (you cant even tell in the movie cause the video is such poor quality sorry) so I quarter the amount of samples I need to make... This algorythm CRASHES the gpu! I get blankouts and I have to alt tab out when the amount of samples im using overloads the video card. ;) Its really simple, takes no maths at all, its just im missing out on the realistic skin, I wonder what more I have to do to get it to that point. Thank you, please comment if your interested. Mine-> (100% realtime) Blender-> [Edited by - rouncED on April 11, 2010 5:39:18 PM]
Advertisement
Wow! Thats amazing!
Demo or code?
cheers.
I spose I could supply the demo if you really wanted me to (give me a message), but I could literally tell you how it works, its not that hard.

It takes about 7 renders, but I do suggest working in low res (like 640x480) cause youll get alot better speed out of it.

Render the model as you would normally with diffuse light only to a separate render target.

Render the model backfaces with diffuse light only to a separate render target.

Render the model with specular only. (some special skin specular you need if you can) You simply add this to the final product at the end.

Render the models front faces and backfaces with a viewspace xyz render, this stores the viewspace positions in a full D3DFMT_A32R32G32B32 floating point texture.

You render onto these charts as many models as you want to scatter.

Then you set the render target to the "sss_out" texture, and take all these textures in as inputs.

Do a full pass of the whole screen with the final shader technique (thats why dropping the res improves speed quite a bit) and itll scatter the entire screens worth of data, you can literally scatter an entire screensworth of environment like this, all in one hit.

For each pixel, make a filled circle of samples, (note you can skip samples and its not so bad on output, youll get a "grain" on the surface of the model) the size of the circle you calculate with a tiny bit of algebra, which converts viewspace coordinates (the scatter distance in world scale) to pixel radius (screen scale) due to the depth of the pixel you check with the viewspace z coordinate.

Once youve got the [approximate] pixel circle radius for each sample, first sample the viewspace position, calculate the distance from the target pixel in world units, and add it to the collected sum of colours weighting it by its distance compared to the chosen scatter distance, it adds 1 close to the target pixel, and adds 0 at the scatter distance, and somewhere inbetween in the partition.

then divide it all by the amount of weight collected at the end, and youll get marble. :)

mix it with the specular on output (specular never scatters in objects and its only ever a part of the direct reflection)

You can advance it a few ways, first way is to do the r,g,b in separate scatter distances and weight them separately. (this is pretty cool, and gives you nice colour gradients on the surface - and its how blender works.)

Another way is to separate the material into 3 layers, with a surface layer, a middle layer, and an inner layer all different colours, and then add blend them together at the end. (thats how mental ray works)

Its actually the biggest shader ive ever written, and it probably takes at least shader 3.0.


I actually coded it by separating the r g b into rings, i did try mental ray but the method doesnt seem to be quite powerful enough to take advantage of that, its better suited to rgb ring.

Heres an example of the shader, note some of the variables are misleading, just follow the logic, its actually half converted to layered style but i changed my mind and converted it back to rgb ring, its actually rgb ring separated, not layer separated, even though the variables are for layer separation and I just use them for another purpose.

All the real scattering happens in "proxblur" thats when i take all the textures as inputs and do the 3d blur.


texture front_col_tex;sampler front_col_map=sampler_state{Texture = <front_col_tex>;MipFilter = NONE;MinFilter = POINT;MagFilter = POINT;AddressU=CLAMP;AddressV=CLAMP;};texture back_col_tex;sampler back_col_map=sampler_state{Texture = <back_col_tex>;MipFilter = NONE;MinFilter = POINT;MagFilter = POINT;AddressU=CLAMP;AddressV=CLAMP;};texture front_xyz_tex;sampler front_xyz_map=sampler_state{Texture = <front_xyz_tex>;MipFilter = NONE;MinFilter = POINT;MagFilter = POINT;AddressU=CLAMP;AddressV=CLAMP;};texture back_xyz_tex;sampler back_xyz_map=sampler_state{Texture = <back_xyz_tex>;MipFilter = NONE;MinFilter = POINT;MagFilter = POINT;AddressU=CLAMP;AddressV=CLAMP;};texture front_pow_tex;sampler front_pow_map=sampler_state{Texture = <front_pow_tex>;MipFilter = NONE;MinFilter = POINT;MagFilter = POINT;AddressU=CLAMP;AddressV=CLAMP;};texture front_spc_tex;sampler front_spc_map=sampler_state{Texture = <front_spc_tex>;MipFilter = NONE;MinFilter = POINT;MagFilter = POINT;AddressU=CLAMP;AddressV=CLAMP;};float power;float3 ldir;float3 res;matrix wvp;matrix rot;matrix view;float3 look;float global_radius;float direct_weight;float3 epi_col;float epi_radius;float epi_weight;float3 sub_col;float sub_radius;float sub_weight;float3 tra_col;float tra_radius;float tra_weight;float spec_weight;float spec1_falloff;float spec1_weight;float spec2_falloff;float spec2_weight;float3 light_pos0;float light_power0;float3 light_pos1;float light_power1;float3 light_pos2;float light_power2;struct vso0{ float4 pos        : POSITION;   //position using the texture coordinates float3 nor        : TEXCOORD0; float4 col        : COLOR0;};struct pso0{ float4 col : COLOR0; };vso0 vs0(float4 pos      : POSITION,         float3 nor      : NORMAL,                        float4 col      : COLOR0){ vso0 output;    output.pos=mul(pos,wvp); output.nor=mul(nor,rot); output.col=col;   return output;    }pso0 ps0(vso0 vso){  pso0 output; float light=dot(vso.nor,ldir); if(light<0) light=0; if(light>1) light=1; output.col.rgb=vso.col.rgb*light; output.col.a=1.0f;  return output;}technique diffuse{ pass P0 {            VertexShader = compile vs_3_0 vs0();  PixelShader  = compile ps_3_0 ps0(); }}struct vso1{ float4 pos        : POSITION;   //position using the texture coordinates float3 nor        : TEXCOORD0;};vso1 vs1(float4 pos      : POSITION,         float3 nor      : NORMAL){ vso1 output;    output.pos=mul(pos,wvp); output.nor=mul(nor,rot);   return output;    }pso0 ps1(vso1 vso){  pso0 output; float Diff = saturate(dot(vso.nor, ldir));  if(Diff<0) Diff=0; if(Diff>1) Diff=1; float sp=(((Diff-0.75f)*4)-0.75f)*4; sp*=spec_weight; output.col=float4(sp,sp,sp,1);  return output;}technique specular{ pass P0 {            VertexShader = compile vs_3_0 vs1();  PixelShader  = compile ps_3_0 ps1(); }}struct vso2{ float4 pos        : POSITION;   //position using the texture coordinates float3 p          : TEXCOORD0;};vso2 vs2(float4 pos      : POSITION){ vso2 output;    output.pos=mul(pos,wvp); output.p=mul(pos,view);   return output;    }pso0 ps2(vso2 vso){  pso0 output; output.col=float4(vso.p.x,vso.p.y,vso.p.z,1);  return output;}technique viewspace{ pass P0 {            VertexShader = compile vs_3_0 vs2();  PixelShader  = compile ps_3_0 ps2(); }}struct vso3{ float4 pos        : POSITION;   //position using the texture coordinates float power       : TEXCOORD0;};vso3 vs3(float4 pos      : POSITION,         float power     : TEXCOORD0){ vso3 output;    output.pos=mul(pos,wvp); output.power=power;   return output;    }pso0 ps3(vso3 vso){  pso0 output; output.col=float4(power,0,0,1);  return output;}technique power{ pass P0 {            VertexShader = compile vs_3_0 vs3();  PixelShader  = compile ps_3_0 ps3(); }}struct vso4{ float4 pos        : POSITION; float2 uv         : TEXCOORD0;};vso4 vs4(float4 pos  : POSITION,         float2 uv   : TEXCOORD0){ vso4 output;    output.pos=pos; output.uv=uv;  return output;    }pso0 ps4(vso4 vso){  pso0 output; float r=0; float g=0; float b=0; float rw=0; float gw=0; float bw=0;  //first off, measure the scatter power float power=tex2D(front_pow_map, vso.uv).r*5; //*5 because you want to make maximum blur distance 5 world units. float3 xyz=tex2D(front_xyz_map, vso.uv).rgb; float3 direct_col=tex2D(front_col_map, vso.uv).rgb*epi_col; float2 s; float2 single=float2(1.0f/res.x,1.0f/res.y); float pixrad=(1075.0f/xyz.z*power)/2; int skip=pixrad/10; //make this 5 a larger number for better quality but more work for the gpu if(skip<1) skip=1;  int p2=pixrad*pixrad;  int i,j; for(i=-pixrad;i<=pixrad;i+=skip) {  for(j=-pixrad;j<=pixrad;j+=skip)  {   if(i*i+j*j<=p2)   {    s=vso.uv+float2(i*single.x,j*single.y);    float3 sxyz=tex2Dlod(front_xyz_map,float4(s,0,0)).rgb;    float3 sxyzb=tex2Dlod(back_xyz_map,float4(s,0,0)).rgb;         float dist;     dist=length(sxyz-xyz);     if(dist>power)    {    }     else    {     float wr=1.0f-(dist/(power*epi_radius));     float wg=(1.0f-(dist/(power*epi_weight)))*20;     float wb=(1.0f-(dist/(power*sub_radius)))*20;       if(wr<0) wr=0;     if(wg<0) wg=0;     if(wb<0) wb=0;       float3 scol=tex2Dlod(front_col_map,float4(s,0,0)).rgb*epi_col;       //float3 sa=abs(tex2Dlod(front_xyz_map,float4(s,0,0)).rgb-tex2Dlod(front_xyz_map,float4(s+float2(single.x,0),0,0)).rgb);       //float ss=(sa.x+sa.y+sa.z)/3;       r+=scol.r*wr;//*ss;     g+=scol.g*wg;//*ss;     b+=scol.b*wb;//*ss;     rw+=wr;     gw+=wg;     bw+=wb;      }    dist=length(sxyzb-xyz);      if(dist>power)    {    }    else    {     float wr=1.0f-(dist/(power*epi_radius));     float wg=(1.0f-(dist/(power*epi_weight)))*20;     float wb=(1.0f-(dist/(power*sub_radius)))*20;       if(wr<0) wr=0;     if(wg<0) wg=0;     if(wb<0) wb=0;       float3 scol=tex2Dlod(back_col_map,float4(s,0,0)).rgb*epi_col;     //float3 sa=abs(tex2Dlod(back_xyz_map,float4(s,0,0)).rgb-tex2Dlod(back_xyz_map,float4(s+float2(single.x,0),0,0)).rgb);       //float ss=(sa.x+sa.y+sa.z)/3;       r+=scol.r*wr;//*ss;     g+=scol.g*wg;//*ss;     b+=scol.b*wb;//*ss;       rw+=wr;     gw+=wg;     bw+=wb;      }   }  } }  if(rw>0) r/=rw; if(gw>0) g/=gw; if(bw>0) b/=bw; //r*=skip*3; //g*=skip*3; //b*=skip*3; float3 mix; mix=float3(r,g,b); output.col=float4(mix.x,mix.y,mix.z,1);  if(mix.x==0 && mix.y==0 && mix.z==0) output.col=float4(0,0,0,0);  return output;}technique proxblur{ pass P0 {            VertexShader = compile vs_3_0 vs4();  PixelShader  = compile ps_3_0 ps4(); }}struct vso5{ float4 pos        : POSITION; float2 uv         : TEXCOORD0;};vso5 vs5(float4 pos  : POSITION,         float2 uv   : TEXCOORD0){ vso5 output;    output.pos=pos; output.uv=uv;  return output;    }pso0 ps5(vso5 vso){  pso0 output; output.col=tex2D(front_col_map, vso.uv)+tex2D(front_spc_map, vso.uv);  return output;}technique display{ pass P0 {            VertexShader = compile vs_3_0 vs5();  PixelShader  = compile ps_3_0 ps5(); }}
Great explanation! Thanks ^^
No worries.

Ill be implementing this again soon, with real strand hair and ill be going for a proper finish instead of these half caked tests, so ill be back dudes :) just give me a couple of months.

This topic is closed to new replies.

Advertisement