CINormalMap_gt (Composition by gtoledo3)

Author: gtoledo3
License: (unknown)
Date: 2011.01.18
Compatibility: 10.4, 10.5, 10.6
Required plugins:

This is a core image filter that takes a greyscale heightfield (bump map) image, and outputs a normal map, in the style of nVidia Normal Map Filter plugin for photoshop, or NMG.

This allows one to create greyscale imagery in Quartz Composer, run image through this filter, and generate normals that can be rendered in the GLSL shader. This example uses Christopher Wright's Normal Map 1.qtz as a rendering destination, to show how it works.

Normal mapping is used to fake the appearance of a higher poly mesh than is actually being used when using GL Lighting.

CINormalMap_gt_demo.qtz96.22 KB
CINormalMap_gt.qtz307.45 KB

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

gtoledo3's picture
Re: CINormalMap_gt (Composition by gtoledo3)

Some usage notes:

  • Don't change x/y from default 1 value unless you understand what you're doing. This changes the schema of what pixels are being sampled to fulfill the equation. So, you can invert a given channel (eg., turn x to -1), and you will be swizzling channels (swapping the expectation of how lighting will render... you'll see shading where you would normally see light).

  • Changing x/y values to be greater (but still equal to one another, and positive, eg. 1.5~2.0), will have an effect somewhat similar to steep parallel mapping.

  • Intensifying the result of the normal map filter by decreasing the value of "adjustment" can intensify results, to prep for smoothing with something like a bilateral or mean filter (or other smoothing algorithm).

  • When dealing with extremely large textures, try run the results of the normal map and source texture through Image Texturing Properties, set destination to 2D, and enable mipmapping (on supported gpu's).

This is an example that shows a depth map captured by a tool similar to the kinect, where greyscale depth map runs through the normal map CI, and then a bilateral filter, before being rendered by the GLSL.

CINormalMap_gt_demo.png502 KB
CINormalMap_gt_demo_teddy example.qtz469.97 KB

benoitlahoz's picture
Re: CINormalMap_gt (Composition by gtoledo3)

Great job ! Thx for this !

gtoledo3's picture
Re: CINormalMap_gt (Composition by gtoledo3)

Thanks. This opens up a bunch of doors for doing stuff in QC that hasn't really been done; I'm not aware of any CI filter, or even GLSL implementation that works in QC for creating normals from a greyscale image, and there wasn't any documentation to go on; just observation, and tedious guessing.

I have to give cwright credit for breaking down the concept of sampling using 4 pixels, vs. a center and 4 around, vs. 9, etc., to me. I understood this from doing box blurs and other documented code, but his encouragement made me feel good about trying it.

It's handy for bringing in some extra details to models, but it's also handy for making a greyscale image in QC through some procedural method, and using that to make a base mesh. Then, you can run that image through another filter, and use that as texture. Finally, you can run the greyscale through another filter (or do a composite operation), to induce the appearance of more polys, dents, and bumps, with a more realistic lighting look in real time. It has cool applications in deferred rendering as well.

I'm attaching an example that's related to the 2.5D Metaball. I'm using a crystallize filter before input to my normal map filter, to create a unique normal map on the fly. This also has a glsl shader that has separate texture/height/normal image inputs.

ProceduralNormalsDemo.qtz48.25 KB

cwright's picture
Re: CINormalMap_gt (Composition by gtoledo3)

I'm super-glad you posted this (on principle) :)

gtoledo3's picture
Re: CINormalMap_gt (Composition by gtoledo3)

I really find the x/y pixel offset controls interesting, because sampling further away from the center pixel increases the effect of triangulation.

Here's the one that samples more pixels. It works better than the other one when pushing it "incorrectly" so that it samples further away from the middle pixel than is strictly correct (which can look cool, and dramatic.)

idlefon's picture
Re: CINormalMap_gt (Composition by gtoledo3)

Cheers GT for posting this. It works perfectly and fast!

By the way I think you did a little mistake in CI_NormalMap_gtII kernel. Shouldn't "TL" be "abs(sample(Image, samplerCoord(Image) + vec2(-x, y)).r);".

Just one question (trying to improve my CI knowledge :D ): What does the line that defines "N" do exactly? Is it making a pixel with "adjust" as it's blue, "RightSidePixels'Red - LefttSidePixels'Red" as it's red and "DownSidePixels'Red - UpSidePixels' Red" as it's green, then normalizing it? If so Why?

Thanks again!

gtoledo3's picture
Re: CINormalMap_gt (Composition by gtoledo3)

Yep, thanks for catching that; that stuff kills me! I fixed it, posted in it's separate place in the repository. (There was another small fix I think, so it bears another dl if you dl'ed it probably.)

I wanted to post it here, and had a million and one versions from when I was getting the channels right... grabbed the wrong one after all of my fussiness and being precious about not wanting to post it "wrong".

gtoledo3's picture
Re: CINormalMap_gt (Composition by gtoledo3)

Yeah, that's what it's doing. It does that because that's a way of encoding coordinates.

In the one example (crud, with one wrong coord in the ci, that kills me... should have looked twice), in the GLSL shader, there is a heightfield input, and that's set to respond so that luminosity makes z vertices extrude.

Now, with something like the normal map, all of those pixels being signed with coordinates helps it, in conjunction with GL Lighting and shader, create the effect of lighting on that "normal". It's creating a "triangle" of point data tip and sides for lighting to respond to.

If you look at color tracking centroid (something like that), there's a coordinate mask over the entire scene at one point. It's a fundamentally similar concept in a different application.