3D Graphics

Yuzhu Lu, Wolfram Research, Inc.

Join the Conversation #WolframTechConf

What Is New With 3D Graphics:

New OpenGL Drawing Layer

New Mesa

Frame Rate Counter

Rendering Engine Choice

Large Dataset

Lighting on Volume Rendering

Mask on Volume Rendering

ClipPlanes

Some New Features in Progress:

Surface Appearance

New OpenGL Drawing Layer

Reasons

1. OpenGL 3.2 standard is not compatible with old fixed function pipeline any more

2. Some code base with different  desktop OpenGL drivers/versions, and mobile OpenGL ES drivers, and possibly WebGL

Challenges

1. hundreds of OpenGL function is removed, we need to implemented them ourselves with the new OpenGL

2. keep the number of OpenGL vertex/fragment shaders under control while adding new features

3. make it work everywhere

4. performance

New Mesa

Mesa is the software renderer used for printing and others

Old Mesa: 7.4.4
New Mesa 10.6.8

Difference

1. New Mesa has a lot of new OpenGL function support, OpenGL 3.0

2. New Mesa is multi-threaded based on LLVM

Grid[{{,"Mesa 7.4.4","Mesa 10.6.8"},{Show[Graphics3D[Sphere[]],PlotLabel->"Single Sphere"],"16.7 fps","128 fps"},{Show[Import["ExampleData/CTengine.tiff","Image3D"],PlotLabel->"Volume Rendering"],"Not Responsive (< 0.1 fps)","22.3 fps"},{Plot3D[Sin[x^2+ y^2],{x,-3,3},{y,-3,3},ColorFunction->(Directive[Opacity[#],Blue]&),PlotPoints->40,Mesh->None, Axes->False,PlotLabel->"Depth Peeling Rendering"],"Not Responsive (< 0.1 fps)","3.8 fps"},{ListPlot3D[ExampleData[{"TestImage","Clock"},"GrayLevels"],PlotStyle->Yellow,Mesh->None,Axes->False,PlotLabel->"More Polygons"],"0.7 fps","12.6 fps"}},Frame->All,ItemStyle->{Automatic,Automatic,{{{1,1},{1,3}}->{Bold,16,RGBColor[0,0,0.8]}}}]

Frame Rate Counter

ShowFrameRateCounter --- A 3D Performance Indicator

1. True: Show the 3D Frame Rate Counter in Window Title
2. False: Do not show the 3D Frame rate Counter

User can set it with SetOptions

SetOptions[$FrontEnd,RenderingOptions->{"ShowFrameRateCounter"->True}]

User can also set it with Option Inspector: Graphics Options / RenderingOptions / “ShowFrameRateCounter”

Rendering Engine Choice

3DRenderingEngine

1. Automatic: Platform dependent default choice: Direct3D on Windows, OpenGL on other paltforms
2. OpenGL: Render with available OpenGL renderer
3. Mesa: Render with in package software renderer Mesa 10.6.8
4. Direct3D: Render with available Direct3D renderer

Graphics3D[ ..., BaseStyle->{ RenderingOptions->{ “3DRenderingEngine”->”OpenGL” } } ]

For this example:

Graphics3D[Sphere[],BaseStyle->{RenderingOptions->{"3DRenderingEngine"->#}},PlotLabel->#]&/@{Automatic,"OpenGL", "Mesa", "Direct3D"}

User can also set it with Option Inspector: Graphics Options / RenderingOptions / “3DRenderingEngine”

Performance

Graphics3D[Sphere[],BaseStyle->{RenderingOptions->{"3DRenderingEngine"->"Direct3D"}}]

Graphics3D[Sphere[],BaseStyle->{RenderingOptions->{"3DRenderingEngine"->"OpenGL"}}]

Graphics3D[Sphere[],BaseStyle->{RenderingOptions->{"3DRenderingEngine"->"Mesa"}}]

Dimensions@ListPlot3D[ExampleData[{"TestImage","Clock"},"GrayLevels"],PlotStyle->Yellow,Mesh->None,Axes->False][[1,1]]

ListPlot3D[ExampleData[{"TestImage","Clock"},"GrayLevels"],PlotStyle->Yellow,Mesh->None,Axes->False,BaseStyle->{RenderingOptions->{"3DRenderingEngine"->"Direct3D"}}]

ListPlot3D[ExampleData[{"TestImage","Clock"},"GrayLevels"],PlotStyle->Yellow,Mesh->None,Axes->False,BaseStyle->{RenderingOptions->{"3DRenderingEngine"->"OpenGL"}}]

ListPlot3D[ExampleData[{"TestImage","Clock"},"GrayLevels"],PlotStyle->Yellow,Mesh->None,Axes->False,BaseStyle->{RenderingOptions->{"3DRenderingEngine"->"Mesa"}}]

Pushing the Limits: Large Dataset

Limits on a 64 bit laptop with 16G RAM

1. Triangles: 35 Million triangles, about 2.5 times bigger with V11.1 than before

Graphics3D[{EdgeForm[None],Polygon[Table[RandomReal[1,{3,3}],{35000000}]]}]

2. points: 100 Million points

Graphics3D[{Point[RandomReal[1,{100000000,3}]]}]

Performance Improvement

Grid[{{Show[,ImageSize->250],"OSX Mac With OpenGL","Windows with Direct3D"},{"V10.3.1","0.1 fps","1.9 fps"},{"V11.1","17.7 fps","55.1 fps"}},Frame->All,ItemStyle->{Automatic,Automatic,{{{1,1},{1,3}}->{Bold,16,RGBColor[0,0,0.8]}}}]

Grid[{{Show[,ImageSize->250],"OSX Mac With OpenGL","Windows with Direct3D"},{"V10.3.1","7 fps","7.9 fps"},{"V11.1","14.3 fps","35.9 fps"}},Frame->All,ItemStyle->{Automatic,Automatic,{{{1,1},{1,3}}->{Bold,16,RGBColor[0,0,0.8]}}}]

Show[Import["blade.ply","Graphics3D"],{PlotLabel->"1.76M triangles with vertex normals", ImageSize->250}]

Grid[{{Show[,ImageSize->250],"OSX Mac With OpenGL","Windows with Direct3D"},{"V10.3.1","0.9 fps","2.6 fps"},{"V11.1","6.6 fps","15.3 fps"}},Frame->All,ItemStyle->{Automatic,Automatic,{{{1,1},{1,3}}->{Bold,16,RGBColor[0,0,0.8]}}}]

Lighting on Volume Rendering

VolumeLighting: Lighting on Raster3D isn’t easy, because there is no polygons and no normals

VolumeLighting is a Method Option of Raster3D: the following values can be used

Raster3D[ ..., Method->{ ”VolumeLighting”->”EnhancedEdge” } ]

Table[Graphics3D[{Specularity[White,6],Raster3D[RawArray["Byte",Import["ExampleData/CThead.tiff","Data"]],{{-1,-1,-1},{1,1,1}},{0,255},ColorFunction->"GrayLevelOpacity",Method->{"VolumeLighting"->t,"InterpolateValues"->True}]},Lighting->{{"Directional",White,ImageScaled[{0,0,2}]}},ViewPoint->{-3.2,0.89,-0.34},ImageSize->250],{t,{False,True,"EnhancedEdge"}}]

Combine Lighting and ColorFunction:

Table[Graphics3D[{Specularity[White,30],Raster3D[RawArray["Byte",Import["ExampleData/CThead.tiff","Data"]],{{-1,-1,-1},{1,1,1}},{0,255},ColorFunction->(Blend[{{0, RGBColor[0.035,0.058,0,0]},{0.1, RGBColor[0.015,0,0.035,0]},{0.2, RGBColor[0,0,0,0.032]},{0.3, RGBColor[0.103,0.032,0,0]},{0.4, RGBColor[0,0,0,0]},{0.5, RGBColor[0.202,0.203,0.218,0.337]},{0.6, RGBColor[0.9,0.9,0.9,0.457]},{0.7, RGBColor[0.98,0.97,1,0.682]},{0.8, RGBColor[1,1,1,0.794]},{0.9, RGBColor[0.95,0.97,0.95,0.842]},{1, RGBColor[1,1,1,0.962]}}, #] &),Method->{ "VolumeLighting"->t,"InterpolateValues"->True}]},ViewPoint->{-3.2,0.89,-0.34},Lighting->{{"Directional",White,ImageScaled[{0,0,2}]}},ImageSize->250],{t,{False,True,"EnhancedEdge"}}]

Any Lighting specification will work with Raster3D:

Table[Graphics3D[{Specularity[White,6],Raster3D[RawArray["Byte",Import["ExampleData/CThead.tiff","Data"]],{{-1,-1,-1},{1,1,1}},{0,255},ColorFunction->"GrayLevelOpacity",Method->{"VolumeLighting"->t,"InterpolateValues"->True}]},Lighting->{{"Spot",RGBColor[{0.882,0.627,0.506}],{{-1,1,0},{0,0,0}},Pi/6,{0,1,0}}},ViewPoint->{-3.2,0.89,-0.34},ImageSize->300],{t,{True,"EnhancedEdge"}}]

Different Point light instruments at the corner points:

lights={PointSize[Large],Yellow,Point[Tuples[{-1,1},3]]};
lighting=({"Point",Hue[RandomReal[]],#}&/@Tuples[{-1,1},3]);

Table[Graphics3D[{lights,Specularity[White,6],Raster3D[RawArray["Byte",Import["ExampleData/CThead.tiff","Data"]],{{-1,-1,-1},{1,1,1}},{0,255},ColorFunction->"GrayLevelOpacity",Method->{"VolumeLighting"->t,"InterpolateValues"->True}]},Lighting->lighting,ViewPoint->{-3.2,0.89,-0.34},ImageSize->300],{t,{True,"EnhancedEdge"}}]

Mask on Volume Rendering

AlphaMask: Apply a Mask to Raster3D

AlphaMask is an Option of Raster3D: the following values can be used

Raster3D[ ..., AlphaMask->maskData ]

mask=DiskMatrix[50,{100,100,100}];

{Graphics3D[{Specularity[White,6],Raster3D[RawArray["Byte",Import["ExampleData/CThead.tiff","Data"]],{{-1,-1,-1},{1,1,1}},{0,255},ColorFunction->"GrayLevelOpacity",Method->{"VolumeLighting"->True,"InterpolateValues"->True}]},Lighting->{{"Directional",White,ImageScaled[{0,0,2}]}},ViewPoint->{-3.2,0.89,-0.34},ImageSize->250],Graphics3D[Raster3D[mask,ColorFunction->"GrayLevelDefaultColorFunction",Method->{"InterpolateValues" -> False, "FastRendering"->True}],ViewPoint->{-3.2,0.89,-0.34},ImageSize->250],Graphics3D[{Specularity[White,6],Raster3D[RawArray["Byte",Import["ExampleData/CThead.tiff","Data"]],{{-1,-1,-1},{1,1,1}},{0,255},ColorFunction->"GrayLevelOpacity",Method->{"VolumeLighting"->True,"InterpolateValues"->True},AlphaMask->mask]},Lighting->{{"Directional",White,ImageScaled[{0,0,2}]}},ViewPoint->{-3.2,0.89,-0.34},ImageSize->250]}

Generate masks with DensityPlot3D and Region functions:

{mask1,mask2,mask3,mask4}=Table[DensityPlot3D[1,{x,y,z}∈region,PlotRange->{{-10,10},{-10,10},{-10,10}}][[1,1]],{region,{BooleanRegion[Or,{Ball[{0,0,0},10],Ball[{-4,0,6},6]}],BooleanRegion[And,{Ball[{0,0,0},10],Ball[{-4,0,6},6]}],BooleanRegion[Xor,{Ball[{0,0,0},10],BooleanRegion[And,{Ball[{0,0,0},10],Ball[{-4,0,6},6]}]}],BooleanRegion[Xnor,{Ball[{0,0,0},10],Ball[{-4,0,6},6]}]}}];

Table[Graphics3D[Raster3D[2m,ColorFunction->"GrayLevelDefaultColorFunction",Method->{"InterpolateValues" -> False, "FastRendering"->True}],ViewPoint->{-3.2,0.89,-0.34},ImageSize->200],{m,{mask1,mask2,mask3,mask4}}]

Table[Graphics3D[{Specularity[White,6],Raster3D[RawArray["Byte",Import["ExampleData/CThead.tiff","Data"]],{{-1,-1,-1},{1,1,1}},{0,255},ColorFunction->"GrayLevelOpacity",Method->{"VolumeLighting"->True,"InterpolateValues"->True},AlphaMask->2m]},Lighting->{{"Directional",White,ImageScaled[{0,0,2}]}},ViewPoint->{-3.2,0.89,-0.34},ImageSize->200],{m,{mask1,mask2,mask3,mask4}}]

Table[Graphics3D[{Specularity[White,30],Raster3D[RawArray["Byte",Import["ExampleData/CThead.tiff","Data"]],{{-1,-1,-1},{1,1,1}},{0,255},ColorFunction->(Blend[{{0, RGBColor[0.035,0.058,0,0]},{0.1, RGBColor[0.015,0,0.035,0]},{0.2, RGBColor[0,0,0,0.032]},{0.3, RGBColor[0.103,0.032,0,0]},{0.4, RGBColor[0,0,0,0]},{0.5, RGBColor[0.202,0.203,0.218,0.337]},{0.6, RGBColor[0.9,0.9,0.9,0.457]},{0.7, RGBColor[0.98,0.97,1,0.682]},{0.8, RGBColor[1,1,1,0.794]},{0.9, RGBColor[0.95,0.97,0.95,0.842]},{1, RGBColor[1,1,1,0.962]}}, #] &),Method->{ "VolumeLighting"->True,"InterpolateValues"->True},AlphaMask->2m]},ViewPoint->{-3.2,0.89,-0.34},Lighting->{{"Directional",White,ImageScaled[{0,0,2}]}},ImageSize->200],{m,{mask1,mask2,mask3,mask4}}]

ClipPlanes

KeyWords: ClipPlanes, ClipPlanesStyle

Graphics3D[…,ClipPlanes->{{(Subscript[a, 1]),(Subscript[b, 1]),(Subscript[c, 1]),(Subscript[d, 1])},…}] specifies a list of clipping planes defined as (Subscript[a, n]) x+(Subscript[b, n]) y+(Subscript[c, n]) z+(Subscript[d, n])≤0. The default value is None.

ClipPlanes can specify up to 36 clipping planes.

ClipPlanes can be specified as directive or as a style option .

ClipPlanes can also take InfinitePlane or ConicHullRegion syntax: ClipPlanes->{InfinitePlane[...],…}

Graphics3D[{Sphere[{0,0,0}],Red,ClipPlanes->InfinitePlane[{{0,0,0},{0,1,0},{1,1,0}}],Sphere[{1,0,0}]}]

Graphics3D[{Sphere[{0,0,0},1/2],Style[Sphere[],Red,ClipPlanes->InfinitePlane[{{0,0,0},{0,1,1},{1,1,2}}]]}]

Some New Features in Progress: SurfaceAppearance

1. Customize Your Own Rendering With Shaders

2. Work with both Graphics and Graphics3D

3. Supported everywhere on all platforms with all drivers including Mesa
4. GLSL 1.2 standard

2D Examples

Customized Image Processing with GPU

FE`Evaluate@Evaluate[FEPrivate`AddSurfaceAppearanceDefinition["My Edge3",{{"position", "ATTRIB_VERTEX"}, {"vertexTextureCoordIn", "ATTRIB_TEXTURECOORD"}},{{"TMatrix","tranformationMatrix",Automatic,"TRANSFORMMATRIX"},{"Color","color",Yellow,"COLOR"},{"Texture2D","texture",Automatic,"TEXTURE"},{"Size","size",360,"NUMBER"}},{"varying vec4 texCoordOut; uniform vec4 color; uniform sampler2D texture; uniform float size; void main() { const float offset=1.0/size;vec4 c=texture2D(texture,texCoordOut.xy); vec4 edge=texture2D(texture,texCoordOut.xy+vec2(-offset,-offset))+texture2D(texture,texCoordOut.xy+vec2(-offset,0.0))+texture2D(texture,texCoordOut.xy+vec2(-offset,offset))+texture2D(texture,texCoordOut.xy+vec2(0.0,offset))+texture2D(texture,texCoordOut.xy+vec2(offset,offset))+texture2D(texture,texCoordOut.xy+vec2(offset,0.0))+texture2D(texture,texCoordOut.xy+vec2(offset,-offset))+texture2D(texture,texCoordOut.xy+vec2(0.0,-offset));gl_FragColor=8.0*(c - 0.125*edge);gl_FragColor.a = 1.0; gl_FragColor *= color;}","attribute vec4 position; attribute vec4 vertexTextureCoordIn; uniform mat4 tranformationMatrix; varying vec4 texCoordOut; void main() { gl_Position = tranformationMatrix * position; texCoordOut = vertexTextureCoordIn;}"}]];

img=ExampleData[{"TestImage","Lena"}];
Manipulate[Graphics[{System`SurfaceAppearance["My Edge3","Color"->Dynamic@c],Texture[img],Polygon[{{-1,-1},{1,-1},{1,1},{-1,1}},VertexTextureCoordinates->{{0,0},{1,0},{1,1},{0,1}}]},ImageSize->{360,360},Frame->True],{c,White}]

Real-Time Video Processing: Processing While Viewing

No Extra CPU Cost, No Extra Memory Cost, Minor Performance Cost

Export["traffic.avi",{,,,,,,,,,}]

ApplyEdges[img_]:=RawBoxes[ToBoxes[img]/.RasterBox[arg_,opts___]:>{System`SurfaceAppearance["My Edge3"],RasterBox[arg,opts]}];

ApplyEdges[ExampleData[{"TestImage","Lena"}]]

ApplyEdges[Import["traffic.avi","Animation"]]

ApplyEdges[ExampleData[{"TestAnimation","ToyVehicles"}]]

GPGPU and Visualization: Julia Factor

FE`Evaluate@Evaluate[FEPrivate`AddSurfaceAppearanceDefinition["My GPGPU3",{{"position", "ATTRIB_VERTEX"}, {"vertexColor", "ATTRIB_COLOR"}},{},{"varying vec2 vPosition;  varying vec2 vJuliaPosition;  void main()  { float cx=vJuliaPosition.x;  float cy=vJuliaPosition.y;  float x=vPosition.x;  float y=vPosition.y;  float hue;  const float saturation=0.6;  const float value=1.0;  float hueRound;  int hueIndex;  float f;  float p;  float q;  float t;  float vel;  float x2=x*x;  float y2=y*y;  float tempX=0.0;  int i=60;  int runaway=0; for (int iter = 0; iter < 60; iter++) { tempX = x2 - y2 + cx; y = 2.0 * x * y + cy; x = tempX; x2 = x * x; y2 = y * y; vel = x2 + y2; if(vel > 10000.0) { i = iter; runaway = 1; break;}} if(runaway == 1)  {hue=(float(i)-log(vel)/9.21)/70.0;  hueRound=hue*6.0; hueIndex=int(mod(float(int(hueRound)),6.0)); f=fract(hueRound); p=value*(1.0-saturation); q=value*(1.0-f*saturation); t=value*(1.0-(1.0-f)*saturation);if (hueIndex == 0) gl_FragColor=vec4(value,t,p,1.0);else if (hueIndex == 1) gl_FragColor=vec4(q,value,p,1.0);else if (hueIndex == 2) gl_FragColor=vec4(p,value,t,1.0);else if (hueIndex == 3) gl_FragColor=vec4(p,q,value,1.0);else if (hueIndex == 4) gl_FragColor=vec4(t,p,value,1.0);else if (hueIndex == 5) gl_FragColor=vec4(value,p,q,1.0);} else gl_FragColor=vec4(0.0,0.0,0.0,1.0);}","attribute vec4 position; attribute vec4 vertexColor; varying vec2 vPosition;  varying vec2 vJuliaPosition;   void main() { gl_Position = vec4(position.xy, 1, 1); vPosition = position.xy * vec2(1.7, 1.2); vJuliaPosition = (vertexColor.xy - 0.5)*2.0;}"}]];

Manipulate[Graphics[{System`SurfaceAppearance["My GPGPU3"],Polygon[{{1.01,1.01},{-1.01,1.01},{-1.01,-1.01},{1.01,-1.01}},VertexColors->{RGBColor[{0.5 +0.5*Sin[0.7*(Dynamic[t]+Sin[Dynamic[t]*0.27]*(Sin[Dynamic[t]*0.55]*0.4+0.4))],0.5 +0.5*Sin[Dynamic[t]+Sin[Dynamic[t]*0.27]*(Sin[Dynamic[t]*0.55]*0.4+0.4)],0,1} ],RGBColor[{0.5 +0.5*Sin[0.7*(Dynamic[t]+Sin[Dynamic[t]*0.27+1.57]*(Sin[Dynamic[t]*0.55]*0.4+0.4))],0.5 +0.5*Sin[Dynamic[t]+Sin[Dynamic[t]*0.27 +1.57]*(Sin[Dynamic[t]*0.55]*0.4+0.4)],0,0}],RGBColor[{0.5 +0.5*Sin[0.7*(Dynamic[t]+Sin[Dynamic[t]*0.27+3*1.57]*(Sin[Dynamic[t]*0.55]*0.4+0.4))],0.5 +0.5*Sin[Dynamic[t]+Sin[Dynamic[t]*0.27 +3*1.57]*(Sin[Dynamic[t]*0.55]*0.4+0.4)],0,1} ],RGBColor[{0.5 +0.5*Sin[0.7*(Dynamic[t]+Sin[Dynamic[t]*0.27+2*1.57]*(Sin[Dynamic[t]*0.55]*0.4+0.4))],0.5 +0.5*Sin[Dynamic[t]+Sin[Dynamic[t]*0.27 +2*1.57]*(Sin[Dynamic[t]*0.55]*0.4+0.4)],0,1}]}]},PlotRange->{{-1,1},{-1,1}},Background->Blue],{t,0,30}]

3D Examples

Toon Shader

FE`Evaluate@Evaluate[FEPrivate`AddSurfaceAppearanceDefinition["My Toon3",{{"position", "ATTRIB_VERTEX"}, {"normal", "ATTRIB_NORMAL"}},{{"WVPMatrix","worldViewProjectionMatrix",Automatic,"WORLDVIEWPROJECTIONMATRIX"},
{"WVITMatrix","worldViewITMatrix",Automatic,"WORLDVIEWITMATRIX"},{"AmbientColor","uAmbientColor",RGBColor[{0.25,0.25,0.25,1}],"COLOR"},{"LightingDirection","uLightingDirection",{1,1,1},"NUMBER3"},{"DirectionalColor","uDirectionalColor",RGBColor[{0.5,0.5,0.5,1}],"COLOR"},{"ToonColor1","toonColor1",RGBColor[{0.804,0.796,0.807,1.0}],"COLOR"},{"ToonColor2","toonColor2",RGBColor[{0.4,0.4,0.8,1.0}],"COLOR"},{"ToonColor3","toonColor3",RGBColor[{0.2,0.2,0.4,1.0}],"COLOR"},{"ToonColor4","toonColor4",RGBColor[{0.086,0.094,0.07,1.0}],"COLOR"}},{"varying float intensity; uniform vec4 toonColor1; uniform vec4 toonColor2; uniform vec4 toonColor3; uniform vec4 toonColor4; void main() {  if(intensity > 0.95) gl_FragColor = toonColor1;  else if(intensity > 0.35)  gl_FragColor = toonColor2; else if(intensity > 0.05)  gl_FragColor = toonColor3; else  gl_FragColor = toonColor4;}","attribute vec4 position; attribute vec4 normal; uniform mat4 worldViewProjectionMatrix; uniform mat4 worldViewITMatrix; uniform vec4 uAmbientColor; uniform vec3 uLightingDirection; uniform vec4 uDirectionalColor; varying float intensity; void main() { gl_Position = worldViewProjectionMatrix * position; vec4 TransformedNormal = worldViewITMatrix * normal; vec3 lightDir = normalize(uLightingDirection); intensity = dot(normalize(TransformedNormal.xyz), lightDir); }"}]];

data2=First@ExampleData[{"Geometry3D","Cow"}];
Manipulate[Graphics3D[{System`SurfaceAppearance["My Toon3","DirectionalColor"->Dynamic@LightColor,"ToonColor1"->Dynamic@ToonColor1,"ToonColor2"->Dynamic@ToonColor2,"ToonColor3"->Dynamic@ToonColor3,"ToonColor4"->Dynamic@ToonColor4, "LightingDirection"->{Dynamic@s[[1]], Dynamic@s[[2]],-1}],data2}],{LightColor,GrayLevel[0.5]},{ToonColor1,RGBColor[0.804,0.796,0.807,1.0]},{ToonColor2,RGBColor[0.4,0.4,0.8,1.0]},{ToonColor3,RGBColor[0.2,0.2,0.4,1.0]},{ToonColor4,RGBColor[0.086,0.094,0.07,1.0]},{{s,{-1,-1}},{0,0},{-2,-2}}]

Combining 2D and 3D Examples

Post Processing: Toon Shader + Edge Detection(Post-Processing)

FE`Evaluate@Evaluate[FEPrivate`AddSurfaceAppearanceDefinition["My Edge3",{{"position", "ATTRIB_VERTEX"}, {"vertexTextureCoordIn", "ATTRIB_TEXTURECOORD"}},{{"TMatrix","tranformationMatrix",Automatic,"TRANSFORMMATRIX"},{"Color","color",Yellow,"COLOR"},{"Texture2D","texture",Automatic,"TEXTURE"},{"Size","size",360,"NUMBER"}},{"varying vec4 texCoordOut; uniform vec4 color; uniform sampler2D texture; uniform float size; void main() { const float offset=1.0/size;vec4 c=texture2D(texture,texCoordOut.xy); vec4 edge=texture2D(texture,texCoordOut.xy+vec2(-offset,-offset))+texture2D(texture,texCoordOut.xy+vec2(-offset,0.0))+texture2D(texture,texCoordOut.xy+vec2(-offset,offset))+texture2D(texture,texCoordOut.xy+vec2(0.0,offset))+texture2D(texture,texCoordOut.xy+vec2(offset,offset))+texture2D(texture,texCoordOut.xy+vec2(offset,0.0))+texture2D(texture,texCoordOut.xy+vec2(offset,-offset))+texture2D(texture,texCoordOut.xy+vec2(0.0,-offset));gl_FragColor=8.0*(c - 0.125*edge);gl_FragColor.a = 1.0; gl_FragColor *= color;}","attribute vec4 position; attribute vec4 vertexTextureCoordIn; uniform mat4 tranformationMatrix; varying vec4 texCoordOut; void main() { gl_Position = tranformationMatrix * position; texCoordOut = vertexTextureCoordIn;}"}]];

data2=First@ExampleData[{"Geometry3D","Cow"}];
Manipulate[With[{v=RotationTransform[θ,{0,0,1}][{3,0,3}],b=Box,lColor=LightColor,tColor1=ToonColor1,tColor2=ToonColor2,tColor3=ToonColor3,tColor4=ToonColor4},Graphics[{System`SurfaceAppearance["My Edge3","Color"->Dynamic@c],Texture[ImageData@Graphics3D[{System`SurfaceAppearance["My Toon3","DirectionalColor"->lColor,"ToonColor1"->tColor1,"ToonColor2"->tColor2,"ToonColor3"->tColor3,"ToonColor4"->tColor4, "LightingDirection"->{-1, -1,-1}],data2},ViewPoint->v,Boxed->b,SphericalRegion->True]],Polygon[{{-1,-1},{1,-1},{1,1},{-1,1}},VertexTextureCoordinates->{{0,0},{1,0},{1,1},{0,1}}],System`SurfaceAppearance[None],EdgeForm[White],Polygon[{{0.5,0.5},{0.98,0.5},{0.98,0.98},{0.5,0.98}},VertexTextureCoordinates->{{0,0},{1,0},{1,1},{0,1}}]},ImageSize->{360,360}]],{LightColor,GrayLevel[0.5]},{ToonColor1,RGBColor[0.804,0.796,0.807,1.0]},{ToonColor2,RGBColor[0.4,0.4,0.8,1.0]},{ToonColor3,RGBColor[0.2,0.2,0.4,1.0]},{ToonColor4,RGBColor[0.086,0.094,0.07,1.0]},{{θ,1},0,2Pi},{Box,{True,False}},{c,White}]