Jump to content

SandardMaterial: Can we just receive shadows but no light


Recommended Posts

Hi BJS team:

I have a little requirement in my project: I want a mesh can recevieShadows but can't be lighted by the light.

Let's say there is a ground (a plane), and some balls moving on it. the balls can cast dynamic shadows on the ground, so, there must a light ( let's say a spot light), a shadow generator, and finally the 'receiveShadows' of the ground is true. 

But the point is, the ground already has a perfectly lighted diffuseTexture which was baked from Blender, so I don't want it be affected by the lights in BJS, but still can receive shadows.

In other words, I want the result of the ground material is only  'DiffuseTexture multiply Shadow'. ( No light calculation, no others... )

Sadly, (correct me if i'm wrong) I didn't find an easy way to achieve this by simply using the built-in StandardMaterial, one thing I found is, in the 'default.fragment.fx', the shadow calculation relies on '#ifdef LIGHT{X}', and the calculated shadow (the 'float shadow' ) was multiplied on the light output, which means: if no lights, no shadows, if you want shadows, you also have the light calculated and affected the final result.

Well, I know I can achieve this by doing some customized shader/material work ( actually I've already done, and it works), but I'm still wondering if there is an easier way to do it. If not, I will wonder if you guys can make the StandardMaterial to support  the case which only need diffuse + shadow, because I guess it's not too difficult to do, correct me if i'm wrong, the main changes should be rearranging some of the  'defines' and '#ifdefs'  so that you can make it possible to calculate shadows even if the disableLighing is true, and then,  multiply the calculated 'float shadow' value directly on the diffuseTexture. 


Link to comment
Share on other sites

Hiya @heyzxz!  Could something here, help? 

In that comment, I fired up a playground demo that uses some light.excludedMeshes in lines 55/56.  I am certainly no expert on the subject, and most of your comment is way over my head, but I thought I would tell you what little I know.  Perhaps you can turn that info into something useful.  Perhaps smarter people than I... will comment soon.  :)

Link to comment
Share on other sites

Thank you @Wingnut. But the 'light.excludedMeshes' doesn't help...

One thing I guess why I got only 1 replier after 2 days  is that I didn't explain my question clear with my bad English :( ...  So please let me explain it again, and I'll try my best to make it as clear as possible... 

Please see the image below:


As you can see, there is a red ground(Plane), a blue ball, and a spot light in the scene. The ball can cast shadow on the ground. The ground only has a pure red diffuseTexture of its material (StandardMaterial). The color of the ground looks gradient (the center looks more bright, and corners look more dark) is only because it is affected by the spot light( in the top center).

All I wanna talk about is the color of the ground. I found the BJS handles it like this (in the 'default.fragment.fx' shader, the fragment shader of the StandardMaterial):


To make it simple, the above diagram is not precise, there are many other operations ignored such as the handling the normal, specular, ambient... But roughly, I think the 'default.fragment.fx' works like this.

#1. Sample the diffuseTexture.

#2. Calculate the light result based on the lights. 

#3. Calculate the shadow result based on the light type, light position... 

Finally, compound all the above 3 results into one as the final output: final = #1 * #2 * #3, (or: final = diffuse * light * shadow). 

Well this work flow is good and make sense for the most cases. But sometimes in some cases,  I think people (just like me) may only want this instead :

final = #1 * #3 (or final =  diffuse * shadow, totally disable the #2)

Let me tell you why...

Sometimes (especially in the real projects), you have a very complex scene built with the Blender( or some other modeling softwares), since there are very complex lights in the scene which can't be supported by BJS, or you have used the Cycles Renderer in the Blender, and you don't want lose too much reality of your scene after exporting to BJS, so what you gonna do? You will probably bake the light effects directly into the diffuseTextures of the models.

Now, you have a perfect diffuseTexture for your gound in the scene with not only diffuseColor but also the light informations (some parts are brighter and some parts are darker ).  But,  there are still some dynamic objects moving on the ground, and now you are considering make your ground can receive dynamic shadows... 

So now, you put a spot light in the scene to generate shadows, but you don't want the ground to change its brightness or darkness by the lights, you only want it receive shadows. In other words, you want to totally disable the light calculation (the #2 above)  but still keep the shadow calculation there. (you want final = diffuse * shadow)

Unfortunately, there is no easy way to achieve this by simply using the StanderdMaterial, because of the current shader logic of the StandardMaterial is designed like this:

// in the lightFragment.fx:
#ifdef LIGHT{X}

	//  #2: Calculate lights...
    // info is the light calculation result

    into = ....
    // ...
	#ifdef SHADOW{X}
	    // #3: Calculate shadows...
        shadow = ...
        // ...
        // Compound shadow and light results, and this result will be compounded with the diffuse later...

		diffuseBase += info.diffuse * shadow;
	// ...

This part has hardcoded that the shadow result MUST compound with the light result. What I want is if the BJS team can change this, make it more flexible so that there is an opportunity to let the user to passby (totally disable) the light calculation and still keep the shadow calculation, and then make the shadow result directly multiply the diffuseColor. By doing this, then I can use StanderMaterial directly with my simple 'reslut = diffuse*shadow' requirement rather than making a custom Material class.

Thank you.













Link to comment
Share on other sites

Ahh, ok, thanks H!  Umm...


I have no solution, but I want to be sure I understand.  In the above demo, I excluded the ground from both lights, and then used .emissive properties on ground.material... to make the ground light itself.  (self-illum).

But now, we still want the ground.receiveShadows to be working, and it is not.  The shadow generators are dependent upon lights.

All in all, we need an option to make the above playground... produce shadows on the ground... with the ground excluded from both lights.  (like the demo is doing except failing at shadows).

Yuh, I have no solution to that.  @Deltakosh, who is the biggest of the big dogs around here, is on vacation at the moment... back next week, I believe.  Maybe he will have some ideas.  Thanks for the clarification... nicely done.  I might have over-simplified the issue, but I need it simple.  :)  Not that I matter, because I don't have a solution.  hmm.  Interesting.

ShadowMaps are calculated from a light's point of view... we can't completely eliminate the lights.  BUT... we still need the lights to NOT affect the ground material in ANY OTHER WAY.   Almost like light.intensity = 0... but make shadows AS IF light.intensity = 1.   We need light.excludedMeshesNotShadows = [ground].  (not available, yet)  :)  hmm.

Link to comment
Share on other sites

Hi @Deltakosh and @heyzxz

  Hey DK...  heyzxz wants to exclude GROUND from lighting color changes... yet still have it RECEIVE shadows (he doesn't care about ground CASTING a shadow).

To reword, he would like normal shadowGenerators, normal ground-receives-shadows, but no lighting changes to ground (other than darkening at shadow silhouettes).

I think this can be done.

IF light-with-shadow-generation is a 3-step process...

   #1 - adjust ground material overall color per light used
   #2 - calc shadowMap
   #3 - apply shadowMap to ground material

... he would like to do step #2 and #3, but avoid step #1.  In the last demo, ground is excluded from all lights (only to keep lights from changing ground color).  But when using excluded mesh, it kills shadows.  So, that won't work.  By setting all lights intensity = 0, ground material stays same color (good) but shadows are still dead.

Pretend @heyzxz makes a ground with material in Blender that has baked light map and is self-illuminated.  He does NOT want any lights to change his ground color.  Now, how does he cast shadows onto that ground (from lights shining on other mesh)?   He can't.  As soon as he turns on a light, the ground changes color.  If he excludes ground mesh from those lights, he can no longer cast any shadows on the ground.

In essence, he wants all shadow systems to act normal, but don't allow any lights to change color of ground (except where there is a shadow, of course).  This seems like a simple modification.  Skip the step that changes the ground color... WITHOUT using light.excludedMeshes = [ground]  (which kills shadows on ground).  EXCLUDE light color changes on ground material, but don't exclude light shadow generating UPON the ground.

light.excludedMeshesThatStillReceiveShadows = [ground]    ?  :)

Have I explained this to death, yet?  :)  Not sure it can be done, but it seems like it is possible.  *shrug*  (I hope I restated the feature request properly, heyzxz)

Link to comment
Share on other sites

Hi @Deltakosh and @Wingnut,

Thank you guys!

 @Wingnut, you explained my request perfectly! :)

And  @Deltakosh, you are pointing me to the right place!  I have read the SimpleMaterial, and now I think start from it will be much easier than what i did before ( making the custom material by modifying the StandardMaterial and its related shaders, really boring, so many uniforms...) The SimpleMaterial looks pure and clear enough, the only thing I need to do is replacing the '#include<lightFragment>[0..maxSimultaneousLights]' to my own 'include' (with lights calculation avoid, only shadows) in the fragment shader.

BTW, a little bug found in the latest simple.vertex.fx:

As the ‘shadowsVertexDeclaration’ and 'shadowsVertex' had changed their code style, the '[0..maxSimultaneousLights]' are missed in the 'simple.vertex.fx' at the parts:

1. '#include<shadowsVertexDeclaration>' (line 50)

2. '#include<shadowsVertex>' (line 90)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...