Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Need help with simple 3D shading

edited September 2013 in Questions Posts: 49

For my game, I'm using this code (taken from the 3d labs) to set up my meshes.

http://pastebin.com/4faU34hi

I don't need anything complicated with reflections and all that, I just need to know how I can change the tint of each side individually.

Here is an example that I pulled off of google images

http://www.bittbox.com/wp-content/uploads/2007/02/illustrator_3d_11.png

The code DOESN'T need to be in "shader format", I just need to know how to change each side's color (fill) individually.

Any help is appreciated!

Tagged:
«1

Comments

  • Jmv38Jmv38 Mod
    Posts: 3,272

    Usually you have to define the 'normal' vector to each tile, V.
    Then you have to define an illumination vector V0.
    These vectors should be nromalized.
    Then you compute for each tile the dot product dp = V0:dot(V).
    If dp <0 then dp =0.
    Then you change the color of you vertext simply multiplying the colors by dp.
    Your object will be correctly shaded.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Jmv38 - I've been looking for the simplest possible example of this, do you have one?

  • Jmv38Jmv38 Mod
    Posts: 3,272

    I'll check what i have.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Jmv38 - I can calculate normals, it's creating the diffuse effect in the fragment shader I'm trying to achieve simply

  • In my ADS lighting class http://twolivesleft.com/Codea/Talk/discussion/2379/ads-lighting-class#Item_1 if you look in the gist at the shaders down the bottom there are examples.

    I probably do a few things more complicated than needed, but lines 251-253 is the diffuse bit of the non-textured example:

     float fDiffuseIntensity = max( c_zero, dot( curNormal, vLightDirection ));
    
        lowp vec4 vDiffuseColor = curCol * lightColor * fDiffuseIntensity * vDiffuseMaterial;
    

    If your light is just white you can omit the lightColor. And curNormal and vLightDirection need to be in the same orientation. I have some complexity to this because I transform light normalised against each triangle and the normal for a flat triangle is then always 90 degrees, but you can instead transform your normals to the same space as the light instead.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @spacemonkey- what do you mean by "curNormal and vLightDirection need to be in the same orientation"?

  • Just to make things clear, I can do the math, I just need to know how to change the colors of the sides individually. I don't need the light source or anything else, just how I change each side. Thanks for all the help!

  • IgnatzIgnatz Mod
    Posts: 5,396

    In that case, make an image with all the different images you will need for the various sides, next to each other in the same image. Make this the mesh texture, then map the texture for each side to the part you want to use for it.

  • Jmv38Jmv38 Mod
    edited September 2013 Posts: 3,272

    @1980geeksquad (pffffew! You should use a shorter alias, this is an ipad keyboard.. ;-). ) To change the color of a mesh vertex, check the mesh.color(i) function. This is used to put shadows to a mesh.

  • The shade needs to be able to change. I'm not just mapping out a house or something, just trying to add a nice effect as the cubes are rotating. Sorry if I'm not clear enough.

    The idea is that you have the camera as the light source, and the sides of the block get darker as their angle to the camera increases (like the picture I posted in the first comment).

    I just need to know how to change the fill (or tint) during gameplay. Sorry if I'm not clear.

  • Jmv38Jmv38 Mod
    Posts: 3,272

    Check my post, simultaneous with yours!

  • edited September 2013 Posts: 49

    How will that help me change the color of each vertex? Sorry, Im kinda new at this.

  • Jmv38Jmv38 Mod
    Posts: 3,272

    Have checked this function in codea built in doc?

  • Jmv38Jmv38 Mod
    Posts: 3,272

    If you know the maths, that should be enough?

  • I got it now! Thank you for the help

  • edited September 2013 Posts: 157

    I was totally going to dig out my shader demo from my graphics class 6 years ago... except now I don't remember where all those files are. =)

    Glad you got it. Shaders are awesome, once you wrap your mind around the concept.

  • Great... It worked when I did two sides but once I added the third side, it said that the color was out of bounds for the first one... I tried removing the third one, and it is still "out of bounds".

        for i=1,6 do
            ms:color(i,0,0,255,255)
        end

    This is what I did... Any help?

  • Never mind. I got rid of ms:setColors. That was the problem

  • I got the colors to change, but they don't update as the variables change. They are set in place when the mesh is created. Is there a way that I can re-call just the color part so the colors update?

  • edited September 2013 Posts: 49

    This is a pick of when the CamAngle starts at 0 and is rotated to 45(ish)
    http://t.co/s2ivXRxqFy

    And this one is when the game starts with CamAngle at 45
    http://t.co/3KBThgKRvb

    Hope that helps a little.

  • Jmv38Jmv38 Mod
    Posts: 3,272

    How do you link the 'variables' to the color in your code?

  • are you using a shader, or setting the colors when you build the mesh?

    what you need to do is set the colors to your base color, then use shaders to light your object.

    Have you looked at the Shader demos yet? If not, I'll try to grab something later.

  • @tomxp411 I think you are right. I have the colors set when the mesh builds. how would I change this?? Im using ms:color(1,CamAngle,255-CamAngle,CamAngle,255) when the mesh is being set up. Is there another way to do it where it doesn't just run at startup?

  • as for shaders, I haven't dove into them at all yet... They kind of confuse me

  • Posts: 2,160

    Use a shader. This is really what shaders are for.

    In your shader, you define an attribute:

    attribute vec3 normal;
    

    Then each vertex gets a normal vector. So for a cube, each vertex would have the normal vector of the face that it is part of (remember that vertex =/= corner in this situation as each corner is repeated several times in the vertices and it will have several possible normal vectors).

    You also need a uniform light vector, and a varying shade.

    uniform vec3 light;
    varying lowp float shade;
    

    Then in the main for the vertex shader, you use the normals to compute the amount of light shining on that face:

    lowp vec4 nor = modelViewProjection * vec4(normal,0);
    vShade = (dot(nor.xyz, light) + 2.)/3.
    

    (This ensures that even the back faces have some light.)

    In the fragment shader you simply use vShade to adjust the colour:

    col.rgb *= vShade;
    

    With that, then the colour on the shape adjusts as it is moved.

  • @Andrew_Stacey I don't know if it is just me, but that went straight over my head...

    I'm fairly new at codea (not new at coding) and the introduction of shaders has caught me off guard... If you know any good tutorials, that would help a lot!

    as for the code you posted, I have no idea how to implement it. I created a new shader and copied what it says, and it threw errors at me like there was no tomorrow.

    When creating a shader, codea automatically gives 3 tabs at the top and a bunch of code, and I haven't the slightest idea on what to do from there.

  • Is there a way that I can just move that part of the code somewhere where it can be run continually? That way I wouldn't have to use shaders

  • @Ignatz I started to get the hang of shaders. Thanks!

    I was also wondering if cartoon shaders would be possible. By that, I mean shaders like borderlands. I guess the proper term is cell shaders.

    here is an example

    http://www.minecraftforum.net/topic/1720766-glsl-shaderpack-naelegos-cel-shaders/

  • IgnatzIgnatz Mod
    Posts: 5,396

    I want those too. I have a great camera app called ToonCamera and I've been trying to replicate its effects. You can get a basic toon effect by greatly reducing the colour palette, eg by rounding every colour value to a multiple of 50, and one of the built in shaders does that (posterize) but that's not very good.

    I've looked at pages like this
    http://www.lighthouse3d.com/tutorials/glsl-tutorial/toon-shading/
    But it's pretty hard stuff....

  • Even just the outline effect would be cool!

    After I understand shaders more, I might try it out a little.

  • Honestly, shaders really are simple. A shader is just a little function that colors one pixel at a time. Seriously: check out the shader examples in Codea. If you've already got the math to calculate normals, you have the mental horsepower you need.

    Actually, I'm surprised there's not a 3D lighting shader already in the library...

  • IgnatzIgnatz Mod
    Posts: 5,396

    @tomxp411 - feel free to write one! :D

  • @Ignatz toon shader link is a good start, but that is the color flattening effect, ie coloring it with fewer colors and becoming a little blocky. This is relatively straight forward although it does rely on first having a lighting model. (also note the example is full openGL and not OpenGL ES)

    But, it doesn't sort out the black highlighting of borders which is a key part of the cartoon look. As far as I know I believe this can only be done by a 2 pass shading approach, where the first pass sorts out the colors and the second does the borders. One problem in codea is if you do a first pass to an offscreen buffer then you don't have a depth part to the buffer, so you can't do borders. However you can hack around it, I did a 2 pass shader for shadows and I encoded the depth in the off screen buffer into the alpha channel and it was workable.

    But 2 pass shaders are another level of complexity so not necessarily a first project thing to do.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @spacemonkey - I would love to have a really simple diffuse example, because I'm trying to build a 3D lunar landing game. I have created 3D terrain but it needs shadows to emphasize the hills.

    So far, my efforts at creating a diffuse light shader, based on something I think you did, just produce a very blocky black and white result. I've even calculated normals by averaging them for across all the surrounding vertices for greater accuracy, but it still looks awful.

    Any suggestions?

  • @Ignatz is your terrain a fixed terrain (ie it doesn't move the vertices around at run time)? I haven't touched Codea for weeks because I've been maxed out on other things, but I might have a play this weekend. I should be able to do something.

    For normals to get a smooth effect you broadly need to: 1) Calculate the lighting in the fragment shader so it differs per pixel. 2) calculate your normals at the vertices based on the average normal for the triangles surrounding that vertex. 3) let the normal varying shader gubbins interpolate your normals for the points between vertices, but renormalise it in the fragment shader before applying the lighting as the interpolation will change it from the unit length.

    Doing something simple should be fairly straight forward.

  • @Ignatz the last time I wrote a shader was 6 years ago, and it was in c++... but I'll pull out that old project and take a look at it. If I can I'll get a sample up for other people to look at.

  • Jmv38Jmv38 Mod
    Posts: 3,272

    @ignatz point (2) of @spacemonkey makes smooth shadows, it is the one i used to get that.

  • Okay, so I think this is the right file. I took a game programming class as an elective when I was getting my CS degree, and all I really learned from it was how to write a shader... which I promptly forgot. =)

    Anyway, this is the shader from my homework project. I can translate it in to something usable in Codea tonight, but for now, here's the raw shader code:


    void vertex( float4 position : POSITION, float4 normal : NORMAL, float4 color : COLOR, uniform float4 rootPosition, uniform float4x4 projection, uniform float4x4 modelView, uniform float4 lightPosition1, out float4 outNormal : TEXCOORD0, out float4 fragPos : TEXCOORD1, out float4 outColor : COLOR, out float4 outPosition : POSITION) { outPosition = mul(projection, position); outColor=color; normal.w=0; outNormal=normalize(normal); fragPos=rootPosition+position; } void fragment( float4 normal : TEXCOORD0, float4 fragPos : TEXCOORD1, float4 diffuseColor : COLOR, // diffuse color uniform float4 ambientColor, uniform float4 specularColor, uniform float shine, uniform float4 dirLightDirection, uniform float4 dirLightColor, uniform float4 lightPosition1, uniform float4 lightColor1, uniform float4 lightPosition2, uniform float4 lightColor2, uniform float4 viewPos, out float4 outColor) { float4 lightVec=0; float4 reflectAngle=0; float4 lightValue; float4 viewVec; float aten; float specularLevel=0; normal.w=0; fragPos.w=0; dirLightDirection.w=0; lightPosition1.w=0; viewVec=normalize(viewPos-fragPos); // directional light lightVec=normalize(dirLightDirection); float level=dot(normal,normalize(dirLightDirection)); lightValue=level * dirLightColor * diffuseColor; // directional light specular highlight reflectAngle=(2*level*normal)-lightVec; // specular angle specularLevel=max((level>0) * dot(reflectAngle,viewVec),0); float4 dirLight = lightValue + pow(specularLevel,shine) * specularColor * dirLightColor; // point light 1 lightVec=normalize(lightPosition1 - fragPos); level=max(dot(normal,lightVec),0); // amplitude of the light aten=1/(1+length(lightPosition1-fragPos)); lightValue=level * aten * diffuseColor; // get the color of the light // specular color reflectAngle=(2*level*normal)-lightVec; // specular angle specularLevel=max((level>0) * dot(reflectAngle,viewVec),0); float4 light1=lightValue+pow(specularLevel,shine) * specularColor * lightColor1; // point light 2 lightVec=normalize(lightPosition2 - fragPos); level=max(dot(normal,lightVec),0); // amplitude of the light aten=1/(1+length(lightPosition2-fragPos)); lightValue=level * aten * diffuseColor; // get the color of the light // specular color reflectAngle=(2*level*normal)-lightVec; // specular angle specularLevel=max((level>0) * dot(reflectAngle,viewVec),0); float4 light2=lightValue+pow(specularLevel,shine) * specularColor * lightColor2; outColor = (ambientColor * diffuseColor) + dirLight+light1+light2; //outColor = light2; }

    Obviously, this is a bit more complex than the simple lighting model the OP was asking for: you can trim this down to a single light source, if you want.

    Feel free to use this any way you want if it's at all helpful.

  • IgnatzIgnatz Mod
    Posts: 5,396

    Yep, that looks like the one I already have, but thank you, every example helps.

    @spacemonkey - yes, it is a static terrain. I'm not renormalising in the fragment shader, maybe that is the problem. (What on earth is gubbins, or was that the iPad spellcheck being helpful?)

    Thanks to all of you. I'll battle on until I get it, then I'll do some tutorials on it for other people, if I can simplify it enough.....

  • edited September 2013 Posts: 157

    okay, I threw together an EXTREMELY quick n dirty test shader to demonstrate this in Codea. This shader only uses directional lighting, and you have to calculate the actual angle of the light yourself. I did that by simply rotating the cube and reading back its model matrix.

    For some reason, I'm not managing to rotate the light angle correctly; so I'll post the Lua part of this after I figured out what's going on there...

    now the vertex shader


    // // A basic vertex shader // //This is the current model * view * projection matrix // Codea sets it automatically uniform mat4 modelViewProjection; uniform vec3 lightAngle; //This is the current mesh vertex position, color and tex coord // Set automatically attribute vec4 position; attribute vec4 color; attribute vec2 texCoord; attribute vec3 normal; //This is an output variable that will be passed to the fragment shader varying lowp vec4 vColor; varying highp vec2 vTexCoord; varying highp vec3 vNormal; varying highp float vLightLevel; void main() { //Pass the mesh color to the fragment shader vColor = color; vTexCoord = texCoord; vNormal = normal; vLightLevel=dot(normal,normalize(lightAngle))*0.5 + 0.5; vColor = vColor * vLightLevel; vColor.a = 1.0; //Multiply the vertex position by our combined transform gl_Position = modelViewProjection * position; }

    and the fragment shader


    // // A basic fragment shader // //Default precision qualifier precision highp float; //This represents the current texture on the mesh uniform lowp sampler2D texture; varying lowp vec4 vColor; varying highp vec2 vTexCoord; varying highp vec3 vNormal; uniform lowp vec3 normal; void main() { //Sample the texture at the interpolated coordinate lowp vec4 col = texture2D( texture, vTexCoord ) * vColor; //Set the output color to the texture color gl_FragColor = col; }
  • IgnatzIgnatz Mod
    Posts: 5,396

    Thanks, Tom. You guys are great. I have to master it, now!

  • IgnatzIgnatz Mod
    Posts: 5,396

    @tomxp411 - what did you mean by "simply rotating the cube and reading back its model matrix"? (Remember I have absolutely no training in 3D graphics!)

  • right, sorry...

    it goes like this: OpenGL uses a 4x4 matrix of numbers to do all the heavy lifting of rotating, translating, and scaling vertices. Every single vertex in your object has to be rotated and translated at least 3 times: you have to put it in the right place in the world, move the world around the camera, and then convert the 3D point to 2D screen coordinates.

    The model matrix is what we use to move models around in the world. Every time you use the rotate() or translate() command, you're manipulating the model matrix.

    So what I'm doing is taking advantage of the model matrix to get the relative angle of the object I'm drawing and the light that's illuminating it.

    boxMesh.shader.lightAngle = vec4(0,1,0,0) -- light comes from straight up
    rotate(rx,1,0.0) -- this tips the box forward by rx degrees
    boxMesh.shader.modelMatrix = modelMatrix()  -- tells the shader what the box's orientation is
    

    This saves the current model matrix to the shader. The shader then takes the matrix and uses it to rotate the light angle. I then compare the light and the normal of each vertex. When the normal is exactly the same as the light angle, the face is 100% illuminated. When the normal faces 90 degrees away from the light, the face is dark.


    // // A basic vertex shader // //This is the current model * view * projection matrix // Codea sets it automatically uniform mat4 modelViewProjection; uniform mat4 modelMatrix; uniform vec4 lightAngle; //This is the current mesh vertex position, color and tex coord // Set automatically attribute vec4 position; attribute vec4 color; attribute vec2 texCoord; attribute vec3 normal; //This is an output variable that will be passed to the fragment shader varying lowp vec4 vColor; varying highp vec2 vTexCoord; varying highp vec3 vNormal; void main() { //Pass the mesh color to the fragment shader vColor = color; vTexCoord = texCoord; vNormal = normal; vec3 l = vec3(lightAngle * modelMatrix); l=normalize(l); vLightLevel=dot(normal,l); vColor = vColor * vLightLevel; vColor.a = 1.0; //Multiply the vertex position by our combined transform gl_Position = modelViewProjection * position; }

    notice the modelMatrix and lightAngle variables at the top. You have to set both of those for each mesh in every draw() cycle.

    Again, I've been away from 3D programming for a while, and I've never used shaders for lighting - I've always had OpenGL's built in lighting engine to do the work for me. So this is kind of new to me. There may be a simpler way to do what I'm doing... I'd love if someone showed me how.

  • Oh, and if you think that's complicated... you don't even want to know what I did to make this work back in the days before hardware 3D acceleration. Drawing one vertex took about 30 mathematical steps.

  • IgnatzIgnatz Mod
    Posts: 5,396

    Thanks!! Much appreciated :D

  • IgnatzIgnatz Mod
    edited September 2013 Posts: 5,396

    @tomxp411, @spacemonkey -

    what I've done is to write up one of spacemonkey's lighting demos in a blog post, and break the code into a step by step project, so I can add the lighting elements one by one, to see the code required for each.

    blog post: http://coolcodea.wordpress.com/2013/09/17/3d-lighting/
    (doesn't cover specular, that's for the next post)
    code: https://gist.github.com/dermotbalson/6589383

    I chose spacemonkey's project as a demo because it is a complete demo, and because it has some interesting features, and he has another one with shiny balls that uses the same shader, that I'd like to cover too. I like the way lighting is used for a 3D effect in that one.

    I'd appreciate any comments you have. I'm still getting across this stuff, and I'm not sure the step by step code approach works (except to identify the code required for different types of light) because the learning curve gets very steep suddenly - but you can't split up the inclusion of diffuse or specular light into pieces across several tabs - it's all or nothing.

    I think this really needs to be explained outside of the code, so my blog post is a first attempt, and I'll tidy it into an ebook version later.

    This is only a start, though. Now I want to compare the differences between @spacemonkey's code and yours, @tomxp411, and make sure I understand all of this. I like the idea of several light sources, too.

    Thanks again for your help.....

  • IgnatzIgnatz Mod
    edited September 2013 Posts: 5,396

    Deleted - answered my own question....

  • =)

    There are multiple types of light, but your tutorial confuses lighting and materials.

    Specular and diffuse are not lighting types, they're material properties. Think about specular highlighting as chrome or shiny stuff... the light doesn't make it shiny, the material itself makes it shiny.

    So the basic types of lights are:
    ambient - all around light. Nothing in the area can be darker than the ambient lighting
    directional - this is the sun. directional light has an infinite distance, so there's no falloff and the light is perfectly perpendicular
    point - point lights will create a relatively small lit area, and point light falls off with distance. points are good for highlighting areas, and that's the basic type of light for lamps, fire, etc

    My code calculates a directional light. Spacemoney's code calculates a point light. They're both valuable and necessary, but they have different artistic uses.

    Now your materials are going to have their own properties: diffuse shading, specular highlights, and maybe be emissive. Emissive textures are great for things like car taillights, where the light needs to be visible but doesn't necessarily need to illuminate an area.

  • IgnatzIgnatz Mod
    Posts: 5,396

    That makes sense, thank you, it's just that everything I read talked about ambient, diffuse and specular, with very little mention of directional vs point.

    I'll rethink it and rework it, thanks again.

Sign In or Register to comment.