I've been having some troubles lately with lighting. I have found a source on google which is working pretty good on the example. However, when I try to implement it to my current project, I am getting some very weird bugs. The main one is that my textures are "mixed up" when I only activate the ambient light, which means that a model gets the texture of another one .
I am using the same effect for every meshes of my models. I guess this could be the problem, but I don't really know how to "reset" an effect for a new model. Is it possible?
Here is my shader:
float4x4 WVP;
float4x4 WVP;
float3x3 World;
float3 Ke;
float3 Ka;
float3 Kd;
float3 Ks;
float specularPower;
float3 globalAmbient;
float3 lightColor;
float3 eyePosition;
float3 lightDirection;
float3 lightPosition;
float spotPower;
texture2D Texture;
sampler2D texSampler = sampler_state
{
Texture = <Texture>;
MinFilter = anisotropic;
MagFilter = anisotropic;
MipFilter = linear;
MaxAnisotropy = 16;
};
struct VertexShaderInput
{
float4 Position : POSITION0;
float2 Texture : TEXCOORD0;
float3 Normal : NORMAL0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 Texture : TEXCOORD0;
float3 PositionO: TEXCOORD1;
float3 Normal : NORMAL0;
};
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output;
output.Position = mul(input.Position, WVP);
output.Normal = input.Normal;
output.PositionO = input.Position.xyz;
output.Texture = input.Texture;
return output;
}
float4 PSAmbient(VertexShaderOutput input) : COLOR0
{
return float4(Ka*globalAmbient + Ke,1) * tex2D(texSampler,input.Texture);
}
float4 PSDirectionalLight(VertexShaderOutput input) : COLOR0
{
//Difuze
float3 L = normalize(-lightDirection);
float diffuseLight = max(dot(input.Normal,L), 0);
float3 diffuse = Kd*lightColor*diffuseLight;
//Specular
float3 V = normalize(eyePosition - input.PositionO);
float3 H = normalize(L + V);
float specularLight = pow(max(dot(input.Normal,H),0),specularPower);
if(diffuseLight<=0) specularLight=0;
float3 specular = Ks * lightColor * specularLight;
//sum all light components
float3 light = diffuse + specular;
return float4(light,1) * tex2D(texSampler,input.Texture);
}
technique MultiPassLight
{
pass Ambient
{
VertexShader = compile vs_3_0 VertexShaderFunction();
PixelShader = compile ps_3_0 PSAmbient();
}
pass Directional
{
PixelShader = compile ps_3_0 PSDirectionalLight();
}
}
And here is how I actually apply my effects:
public void ApplyLights(ModelMesh mesh, Matrix world,
Texture2D modelTexture, Camera camera, Effect effect,
GraphicsDevice graphicsDevice)
{
graphicsDevice.BlendState = BlendState.Opaque;
effect.CurrentTechnique.Passes["Ambient"].Apply();
foreach (ModelMeshPart part in mesh.MeshParts)
{
graphicsDevice.SetVertexBuffer(part.VertexBuffer);
graphicsDevice.Indices = part.IndexBuffer;
// Texturing
graphicsDevice.BlendState = BlendState.AlphaBlend;
if (modelTexture != null)
{
effect.Parameters["Texture"].SetValue(
modelTexture
);
}
graphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
part.VertexOffset,
0,
part.NumVertices,
part.StartIndex,
part.PrimitiveCount
);
// Applying our shader to all the mesh parts
effect.Parameters["WVP"].SetValue(
world *
camera.View *
camera.Projection
);
effect.Parameters["World"].SetValue(world);
effect.Parameters["eyePosition"].SetValue(
camera.Position
);
graphicsDevice.BlendState = BlendState.Additive;
// Drawing lights
foreach (DirectionalLight light in DirectionalLights)
{
effect.Parameters["lightColor"].SetValue(light.Color.ToVector3());
effect.Parameters["lightDirection"].SetValue(light.Direction);
// Applying changes and drawing them
effect.CurrentTechnique.Passes["Directional"].Apply();
graphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
part.VertexOffset,
0,
part.NumVertices,
part.StartIndex,
part.PrimitiveCount
);
}
}
I am also applying this when loading the effect:
effect.Parameters["lightColor"].SetValue(Color.White.ToVector3());
effect.Parameters["globalAmbient"].SetValue(Color.White.ToVector3());
effect.Parameters["Ke"].SetValue(0.0f);
effect.Parameters["Ka"].SetValue(0.01f);
effect.Parameters["Kd"].SetValue(1.0f);
effect.Parameters["Ks"].SetValue(0.3f);
effect.Parameters["specularPower"].SetValue(100);
Thank you very much
UPDATE:
I tried to load an effect for each model when drawing, but it doesn't seem to have changed anything. I suppose it is because XNA detects that the effect has already been loaded before and doesn't want to load a new one. Any idea why?
Alright so the problem was pretty stupid actually. I was applying the same effect to every meshes. I simply added a Effect.Clone() when loading the model and it worked!
Related
I would like to convert a DepthInformation into a position.
Somehow I can't do it even after several attempts.
I try to get the position of objects fragment from the DepthInformation that i get from an Depthpeeling shader.
This is the code writing the information in a RenderTexture
Shader "Hidden/OIT/Depth Peeling/Depth Peeling" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
}
SubShader {
Tags {"Queue"="Geometry" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
sampler2D _PrevDepthTex;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 lightDir: TEXCOORD0;
float3 viewDir : TEXCOORD1;
float2 uv : TEXCOORD2;
float4 screenPos: TEXCOORD3;
float depth : TEXCOORD4;
};
struct PixelOutput {
fixed4 col : COLOR0;
fixed4 depth : COLOR1;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
TANGENT_SPACE_ROTATION;
// Transform the light direction from object space to tangent space
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
// Transform the view direction from object space to tangent space
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
o.screenPos = ComputeScreenPos(o.pos);
o.depth = COMPUTE_DEPTH_01;
return o;
}
PixelOutput frag(v2f i) : SV_Target {
float depth = i.depth;
float prevDepth = DecodeFloatRGBA(tex2Dproj(_PrevDepthTex, UNITY_PROJ_COORD(i.screenPos)));
clip(depth - (prevDepth + 0.00001));
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uv));
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
PixelOutput o;
o.col = fixed4(ambient + diffuse, _Color.a);
o.depth = EncodeFloatRGBA(i.depth);
return o;
}
ENDCG
}
}
FallBack "Diffuse/VertexLit"
}
and this is the function where i try to decode it again to a world position:
float4 DepthTextureToPos(sampler2D depthSampler,float2 uvCoord,float4 screenPos) {
float depth = DecodeFloatRGBA(tex2Dproj(depthSampler, UNITY_PROJ_COORD(screenPos)));
// H is the viewport position at this pixel in the range -1 to 1.
float4 H = float4((uvCoord.x) * 2 - 1, (uvCoord.y) * 2 - 1, depth, 1.0);
float4 D = mul(_ViewProjectInverse, H);
//return D / D.w;
float4 objPos= mul(unity_WorldToObject, (D / D.w));
return objPos;
}
the uv Coords have this as input:
float2 texc = i.screenPosition.xy / i.screenPosition.w;
the screenPos has this as Input:
ComputeScreenPos(o.vertex);
I found shader code that has the effect of warping a space around a certain point. It's a cool effect, but it's missing some animation, so I've added something to it:
Shader "Marek/BlackHoleDistortion"
{
Properties {
_DistortionStrength ("Distortion Strength", Range(0, 10)) = 0
_Timer("Timer", Range(0, 10)) = 0
_HoleSize ("Hole Size", Range(0, 1)) = 0.1736101
_HoleEdgeSmoothness ("Hole Edge Smoothness", Range(1, 4)) = 4
_ObjectEdgeArtifactFix ("Object Edge Artifact Fix", Range(1, 10)) = 1
}
SubShader {
Tags {
"IgnoreProjector"="True"
"Queue"="Transparent"
"RenderType"="Transparent"
}
GrabPass{ }
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
ZWrite Off
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#pragma only_renderers d3d9 d3d11 glcore gles
#pragma target 3.0
uniform sampler2D _GrabTexture;
uniform float _DistortionStrength;
uniform float _HoleSize;
uniform float _HoleEdgeSmoothness;
uniform float _ObjectEdgeArtifactFix;
uniform float _Timer;
struct VertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float4 posWorld : TEXCOORD0;
float3 normalDir : TEXCOORD1;
float4 projPos : TEXCOORD2;
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
o.projPos = ComputeScreenPos(o.pos);
COMPUTE_EYEDEPTH(o.projPos.z);
return o;
}
float4 frag(VertexOutput i) : COLOR {
i.normalDir = normalize(i.normalDir);
float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
float3 normalDirection = i.normalDir;
float2 sceneUVs = (i.projPos.xy / i.projPos.w);
float node_9892 = (_HoleSize * -1.0 + 1.0);
float node_3969 = (1.0 - pow(1.0 - max(0, dot(normalDirection, viewDirection)), clamp(_DistortionStrength - _Timer, 0, _DistortionStrength)));
float node_9136 = (length(float2(ddx(node_3969), ddy(node_3969))) * _HoleEdgeSmoothness);
float node_4918 = pow(node_3969, 6.0);
float node_1920 = (1.0 - smoothstep((node_9892 - node_9136), (node_9892 + node_9136), node_4918));
float3 finalColor = (
lerp(
float4(node_1920, node_1920, node_1920, node_1920),
float4(1, 1, 1, 1),
pow(
pow(1.0 - max(0, dot(normalDirection, viewDirection)), 1.0),
_ObjectEdgeArtifactFix
)
) * tex2D(_GrabTexture, ((node_4918 * (sceneUVs.rg * _Time * -2.0 + 1.0)) + sceneUVs.rg)).rgb).rgb;
return fixed4(finalColor, 1);
}
ENDCG
}
}
FallBack "Diffuse"
}
Now, the problem is that in order to make the distortion disappear after certain time, I need to include some variable into the equation - here I'm calling it _Timer. I'm not using the _Time built in because of obvious reasons - it's an ever growing value and I need something that starts from 0 each time the object using this shader is made active. C# code handling passing that parameter looks as follows:
public void Update() {
_timeElapsed += Time.deltaTime;
_renderer.material.SetFloat("_Timer", _timeElapsed);
}
The question is - can I do it better? I would like this shader's code to be more of a self-contained thing - without the need to pass parameters from cs script to it.
Can I do it better?
In-short, yes and no. If you want the shader to behave differently per material you simply cannot avoid passing a property from C#. You can however avoid doing this in Update by passing a start time and computing the elapse time in the shader.
C#
void OnEnable ()
{
_renderer.material.SetFloat("_StartTime", Time.timeSinceLevelLoad);
}
Shader
uniform float _StartTime;
float4 frag(VertexOutput i) : COLOR
{
float elapse = _Time.y - _StartTime;
}
Now, although this will tie directly into the setup you are currently using, it should be noted that accessing the .material property will clone the material (which can break batching, among other things).
This can be avoided with the more recent introduction of MaterialPropertyBlocks.
Unity provides a handful of built-in values for your shaders: things like current object’s transformation matrices, time etc.
You just use them in ShaderLab like you’d use any other property, the only difference is that you don’t have to declare it somewhere - they are “built in”.
https://docs.unity3d.com/455/Documentation/Manual/SL-BuiltinValues.html
there is a clever way of giving you 4 variations of the value, potentially saving you a multiply operation by re-using the pre-multiplied value for every pixel being rendered. There are 4 values available.
_Time.x = time / 20
_Time.y = time
_Time.z = time * 2
_Time.w = time * 3
this is a simple example that show you how it works:
Shader "Example/Circle"
{
Properties
{
}
SubShader
{
Cull Off
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;
}
float circle(in float2 _st, in float _radius){
float2 dist = distance(_st,float2(0.5,0.5));
float result = step(dist,_radius);
return result;
}
fixed4 frag (v2f i) : SV_Target
{
float WaveTime = sin(_Time.z);
float3 color = float3(1,1,1)*circle(i.uv,WaveTime);
return float4( color, 1.0 );
}
ENDCG
}
}
}
In comments you mentioned that you want reset time value when you enable it , so here you need to Initialize Time value with script.
so you should use your own Time In shader:
Properties
{
_Timer("Timer",Float) = 0
}
float WaveTime = sin(_Timer);
using System.Collections;
using UnityEngine;
public class Circle : MonoBehaviour {
public float _timeElapsed;
void OnEnable(){
_timeElapsed = 0;
}
public void Update() {
_timeElapsed += Time.deltaTime;
var _renderer = GetComponent<MeshRenderer>();
_renderer.material.SetFloat("_Timer", _timeElapsed);
}
}
So I have this shader that I am trying to make flicker, so I am making the emission value go back and forth between 0 and 1, but it only works if I click on the material or scroll in the editor. I can't figure this out, help please?
I checked something similar to this, but it still won't work, so please help.
Renderer renderer;
Material material;
float emission;
// Use this for initialization
void Start () {
renderer = GetComponent<Renderer>();
material = renderer.material;
}
// Update is called once per frame
void Update () {
emission = Mathf.PingPong (Time.time, 1f);
Color color = Color.red;
Color finalColor = color * Mathf.LinearToGammaSpace (emission);
material.SetColor ("_EmissionColor", finalColor);
}
i solved a problem like you
pleas share your Shader to us thanks
try Mathf.sin(Time.time) to get pingpong maybe your problem Solved
or
use blow shader
Shader "Custom/Emition" {
Properties {
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_EmissionColor ("Bright", Float) = 1.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform float _EmissionColor;
sampler2D _MainTex;
struct vertexInput {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
output.uv = input.texcoord;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
fixed4 c = tex2D (_MainTex , input.uv) ;
/// change blow line
return c* float4(_EmissionColor,1,1,1);
}
ENDCG
}
}
FallBack "Diffuse"
}
I have a really big problem that has been bothering me for so long and I can't seem to find the solution. I have downloaded this project https://github.com/brianasu/unity-ray-marching/tree/volumetric-textures (Unitypackage with my project here https://dl.dropboxusercontent.com/u/27758186/ApplicationVolume.unitypackage ) , that is about volume rendering.
The problem is that, as you can see, the volume renders ALWAYS in front of everything else (try placing a cube in front of the cube with the volume). I have tried a lot of things but none seems to work.
I think it might be an issue of the shaders used. In the main Camera, a RayMarching script is attached, which contains an OnRenderImage method, which creates a new Camera (although disabled), and renders the volume.
I don't know if it is a shader issue, then, or more like a camera issue (is rendering with replaced shaders). I attach my current project for testing so you don't have to download from git, and I just want the volume (head), to appear behind the rectangles (not seen) when it's actually behind, and in front when it is, pretty much like an standard geometry, but it appears always on top...
Any help or suggestion would be GREATLY appreciated, I'm kind of desperate as anything works and I'm pretty sure it is a fairly easy issue.
The code of the shader of the Ray Marching is as follows. Should it do some kind of ZTesting to not show the fragments that are covered by any other object?
Shader "Hidden/Ray Marching/Ray Marching"
{
CGINCLUDE
#include "UnityCG.cginc"
#pragma target 3.0
#pragma profileoption MaxLocalParams=1024
#pragma profileoption NumInstructionSlots=4096
#pragma profileoption NumMathInstructionSlots=4096
struct v2f {
float4 pos : POSITION;
float2 uv[2] : TEXCOORD0;
};
sampler3D _VolumeTex;
float4 _VolumeTex_TexelSize;
sampler2D _FrontTex;
sampler2D _BackTex;
float4 _LightDir;
float4 _LightPos;
float _Dimensions;
float _Opacity;
float4 _ClipDims;
float4 _ClipPlane;
v2f vert( appdata_img v )
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv[0] = v.texcoord.xy;
o.uv[1] = v.texcoord.xy;
#if SHADER_API_D3D9
if (_MainTex_TexelSize.y < 0)
o.uv[0].y = 1-o.uv[0].y;
#endif
return o;
}
#define TOTAL_STEPS 128.0
#define STEP_CNT 128
#define STEP_SIZE 1 / 128.0
half4 raymarch(v2f i, float offset)
{
float3 frontPos = tex2D(_FrontTex, i.uv[1]).xyz;
float3 backPos = tex2D(_BackTex, i.uv[1]).xyz;
float3 dir = backPos - frontPos;
float3 pos = frontPos;
float4 dst = 0;
float3 stepDist = dir * STEP_SIZE;
for(int k = 0; k < STEP_CNT; k++)
{
float4 src = tex3D(_VolumeTex, pos);
// clipping
float border = step(1 - _ClipDims.x, pos.x);
border *= step(pos.y, _ClipDims.y);
border *= step(pos.z, _ClipDims.z);
border *= step(0, dot(_ClipPlane, float4(pos - 0.5, 1)) + _ClipPlane.w);
// Standard blending
src.a *= saturate(_Opacity * border);
src.rgb *= src.a;
dst = (1.0f - dst.a) * src + dst;
pos += stepDist;
}
return 3.0F*dst;// + dst;
}
ENDCG
Subshader {
Fog { Mode off }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
half4 frag(v2f i) : COLOR {
return raymarch(i, 0);
}
ENDCG
}
}
Fallback off
} // shader
I'm currently using shaders in my game, it's working fine with a nVidia GeForceGT330m but with an ATI 4670 (which supports ps_4.1) I encounter a black screen.
Here is the source of the HLSL effect:
struct Explo
{
float3 position;
float4 color;
float power;
int time;
};
float2 DisplacementScroll;
texture colortexture;
int nb;
Explo explos[5];
float ambient;
float4 ambientColor;
float screenWidth;
float screenHeight;
sampler ColorMap = sampler_state
{
Texture = <colortexture>;
};
float4 CalculateLight(Explo ex, float4 base, float3 pixelPosition)
{
float3 direction = ex.position - pixelPosition;
float distance = 1 / length(ex.position - pixelPosition) * ex.power;
float amount = max(dot(base, normalize(distance)), 0);
return base * distance * amount * ex.color * ambient;
}
float4 Explosion(float2 texCoords : TEXCOORD0) : COLOR
{
//texCoords = tex2D(NormalMap, DisplacementScroll + texCoords / 3)*0.2 - 0.15;
float4 base = tex2D(ColorMap, texCoords);
float3 pixelPosition = float3(screenWidth * (texCoords.x),
screenHeight * (texCoords.y),0);
float4 finalColor = (base * ambientColor * ambient);
for (int i=0; i<nb; i++)
{
finalColor += CalculateLight(explos[i], base, pixelPosition);
}
return finalColor;
}
technique KaBoom
{
pass Pass1
{
PixelShader = compile ps_3_0 Explosion();
}
}
I remember once I had a similar problem. A shader just didn't work on ATI. The problem was that the vertex and pixel shaders were compiled to different shader models (vs_3_0 and ps_2_0). It worked for NVIDIA, but not for ATI. In your case you're only binding a pixel shader for the pass and who knows what the last vertex shader was.
Granted, this is relevant only if you're dead sure the problem is with the shader and not something else, e.g. your DIPs.
Good luck