Visualization: Advanced 3D Graphics

A walkthrough of 3D computer graphics

Yu-Sung Chang
Wolfram Research
wolfram-u@wolfram.com

Real World Stage

Graphics World Scene

DynamicModule[{light,scene=False,screen=False,lighting=False,x=0},

light[p1_,p2_,r_]:=With[{v=Normalize[p2-p1]},
GeometricTransformation[
GraphicsGroup[{
EdgeForm[],
GrayLevel[.1],Specularity[Gray,10],
CapForm[None],
Tube[{{0,0,0},{0,0,2r}},r],
GeometricTransformation[Polygon[{{r,-.5r,1.95r},{r,.5r,1.95r},{(1.5)r,.7r,(3)r},{(1.5)r,-.7r,(3)r}}],Table[RotationTransform[Pi t/2,{0,0,1}],{t,0,3}]],
CapForm["Butt"],
Tube[{{0,0,0},{0,0,.01r}},r],
White,Specularity[Yellow,10],
Tube[{{0,0,1.6 r},{0,0,1.61r}},.999r]
}],
Composition[TranslationTransform[p1],RotationTransform[{{0,0,1},v},{0,0,0}]]]
];

Panel[
Column[{
With[{lp1={1,-.55,.775},lp2={.8,-.55,.775},lp3={1.2,-.55,.775},center={1,0,.3}},
Show[
(* Brick wall *)
ParametricPlot3D[{2 x,0,z},{x,0,1},{z,0,1},PlotStyle->Directive[Texture[]],TextureCoordinateFunction->({12#4,6#5}&),PlotPoints->50,MaxRecursion->0,Mesh->None,ViewAngle->0.23,ViewCenter->{{0.5,0.5,0.5},{0.5,0.56}},ViewPoint->{0,-2,0.0},ViewVertical->{0,0,2},RotationAction->"Clip",ImageSize->{750,500},Lighting->None,PerformanceGoal->"Speed"]/.{GraphicsComplex[args___]:>Style[{Dynamic[If[scene,{},FaceForm[]]],GraphicsComplex[args]},Lighting->{
{"Spot",White,{{0,-2,.5},{0,0,.5}},{0,15},{0,0,1}},{"Spot",White,{{2,-2,.5},{2,0,.5}},{0,15},{0,0,1}}}]},

(* Floor *)
ParametricPlot3D[{2x,-y,0},{x,0,1},{y,0,1},PlotStyle->Directive[Specularity[White,10],Texture[ExampleData[{"ColorTexture","Amboyna"}]]],TextureCoordinateFunction->({15#4,2#5}&),PlotPoints->50,MaxRecursion->0,Mesh->None,PerformanceGoal->"Speed"]/.{GraphicsComplex[args___]:>Style[{Dynamic[If[scene,{},FaceForm[]]],GraphicsComplex[args]},Lighting->{
{"Spot",White,{{0,-.5,1},{0,-.5,0}},{0,10},{0,0,2}},
{"Spot",White,{{1,-.5,1},{1,-.5,0}},{0,25},{0,0,1}},
{"Spot",White,{{2,-.5,1},{2,-.5,0}},{0,10},{0,0,2}}}]},

(* Screen *)
ParametricPlot3D[{2x,-.1-If[z<.05,Sqrt[((.05)^2)-(((z-.05))^2)],.05],z},{x,.25,.75},{z,0,.8},PlotStyle->White,PlotPoints->80,MaxRecursion->0,Mesh->None,PerformanceGoal->"Speed"]/.{GraphicsComplex[vtx_,args___]:>Style[{Dynamic[If[screen,{},FaceForm[]]],GraphicsComplex[vtx,args]},Lighting->{
{"Spot",Dynamic[If[lighting,GrayLevel[.5],Black]],{lp1,center},{0,10},{0,0,1}},
{"Ambient",Dynamic[If[lighting,Darker[LightYellow,.1],Darker[LightYellow,.2]]]}}]},

(* Screen hanger *)
Graphics3D[Style[
With[{p1={2 .25-.01,-.15,.8},p2={2 .75+.01,-.15,.8},r=0.005},
{Dynamic[If[screen,{},FaceForm[]]],
Black,Specularity[Gray,100],CapForm["Round"],
Tube[{p1,p1+{0,.15,0}},r],
Tube[{p1,p1+{0,.15,-.1}},r],
Tube[{p2,p2+{0,.15,0}},r],
Tube[{p2,p2+{0,.15,-.1}},r],
ColorData["HTML","Peru"],
Tube[{p1,p2},2r]
}],Lighting->{
{"Point",White,{1,-.5,1}},
{"Directional",White,{0,0,1}},{"Directional",White,{3,0,1}}}]],

(* Lights *)
Graphics3D[
Style[{
Dynamic[If[lighting,{},FaceForm[]]],
light[lp1,center,.025],
light[lp2,center,.025],
light[lp3,center,.025]
},Lighting->{{"Directional",White,{1,-.5,0}}}]],

(* Trust *)
Graphics3D[
DynamicModule[{r=0.003,p1={.5,-.5,.85},p2={1.5,-.5,.85},p3={.5,-.5,.8},p4={1.5,-.5,.8},p5,p6,p7,p8},

{p5,p6,p7,p8}=(#-{0,.1,0})&/@{p1,p2,p3,p4};

Style[{
Dynamic[If[lighting,{},FaceForm[]]],
Black,Specularity[White,10],
EdgeForm[],CapForm[None],
(* Rectangle structure *)
Tube[{p1,p2,p4,p3,p1},r],Tube[{p5,p6,p8,p7,p5},r],
(* Cross bars *)
Table[Tube[{p1+{.1i,0,0},p3+{.05+.1i,0,0}},r],{i,0,9}],
Table[Tube[{p3+{-.05+.1i,0,0},p1+{.1i,0,0}},r],{i,10}],
Table[Tube[{p5+{.1i,0,0},p7+{.05+.1i,0,0}},r],{i,0,9}],
Table[Tube[{p7+{-.05+.1i,0,0},p5+{.1i,0,0}},r],{i,10}],
(* Horizontal bars *)
White,
Tube[{p3,p7},r],Tube[{p4,p8},r],
Table[Tube[{p1+{.1i,0,0},p5+{.1i,0,0}},r],{i,0,10}],
Table[Tube[{p3+{-.05+.1i,0,0},p7+{-.05+.1i,0,0}},r],{i,10}],
(* Hanging wire *)
ColorData["HTML","LightSteelBlue"],
Tube[{p1,p1+{0,0,2}},.8r],Tube[{p1+{.5,0,0},p1+{.5,0,2}},.8r],
Tube[{p2,p2+{0,0,2}},.8r],
Tube[{p5,p5+{0,0,2}},.8r],Tube[{p5+{.5,0,0},p5+{.5,0,2}},.8r],
Tube[{p6,p6+{0,0,2}},.8r]
},
Lighting->{{"Directional",White,{1,-.5,0}}}]
]
],

(* Actor *)
ExampleData[{"Geometry3D","Cow"}]/.{GraphicsComplex[vtx_,args___]:>Style[{Dynamic[If[screen,FaceForm[Directive[Specularity[White,15],ColorData["HTML","GoldenRod"]]],FaceForm[]]],Translate[Scale[GraphicsComplex[vtx,args],.5],Dynamic[{2x,-.35,.125}]]},Lighting->{
{"Spot",GrayLevel[1],{lp1+{0,-1.25,0},center},{0,10},{0,0,1}},
{"Ambient",GrayLevel[.1]}}]},

PlotRange->{{0,2},{-1,0},{0,1}},
Boxed->False,
Axes->False,Background->Dynamic[If[scene,Gray,White]]]
],

"",

(* Controls *)
Row[{"Room " ,Checkbox[Dynamic[scene]],Spacer[20],"Screen ",Checkbox[Dynamic[screen]],Spacer[20],"Lighting ",Checkbox[Dynamic[lighting]],Spacer[20],"Action ",Slider[Dynamic[x],{0,1}]},BaseStyle->{FontFamily->"Arial",14}]
}]
]
]

Predefined Lighting

Graphics3D[Sphere[],Lighting->Automatic]

Graphics3D[Sphere[],Lighting->"Neutral"]

Actual values

Graphics3D[Sphere[],Lighting->{{"Ambient",RGBColor[{0.312,0.188,0.4}]},{"Directional",RGBColor[{0.8,0.,0.}],ImageScaled[{2,0,2}]},{"Directional",RGBColor[{0.,0.8,0.}],ImageScaled[{2,2,2}]},{"Directional",RGBColor[{0.,0.,0.8}],ImageScaled[{0,2,2}]}}]

Graphics3D[Sphere[],Lighting->Lighting->{{"Ambient",RGBColor[{.35,.35,.35}]},{"Directional",RGBColor[{.37,.37,.37}],ImageScaled[{2,0,2}]},{"Directional",RGBColor[{.37,.37,.37}],ImageScaled[{2,2,2}]},{"Directional",RGBColor[{.37,.37,.37}],ImageScaled[{0,2,2}]}}]

Light Types

Ambient

Directional

Point

Spot

Ambient Light

Graphics3D[Sphere[],Lighting->{{"Ambient",Red}}]

Manipulate[Graphics3D[Sphere[],Lighting->{{"Ambient",color}}],{color,Red}]

Directional Light: Basics

Graphics3D[Sphere[],Lighting->{{"Directional",White,{{0,0,5},{0,0,0}}}}]

Graphics3D[Sphere[],Lighting->{{"Directional",Red,{2,0,0}},{"Directional",Green,{0,-2,0}},{"Directional",Blue,{0,0,2}}}]

Directional Light: Properties

Direction matters

Manipulate[
With[{pt=6{Cos[θ]Cos[ρ],Sin[θ]Cos[ρ],Sin[ρ]},center={0,0,0}},
Show[
Graphics3D[{
Specularity[White,50],
Sphere[{#[[1]],#[[2]],1}]&/@Tuples[{-2,2},2],
Dynamic[If[show,{color,Style[Cylinder[{pt,1.05pt},.5],Lighting->"Neutral"]},{}]],
Opacity[.1],Sphere[center,6]},Lighting->{{"Directional",color,{pt,center}}}],

dirplane,

Graphics3D[Dynamic[If[show,{Gray,Dashed,Line[N@Table[6{Cos[t]Cos[ρ],Sin[t]Cos[ρ],Sin[ρ]},{t,0,2 π,((2 π)/50)}]]},{}]]],

Graphics3D[Dynamic[If[show,{Gray,Dashed,Line[N@Table[6{Cos[θ]Cos[s],Sin[θ]Cos[s],Sin[s]},{s,0,Pi},{s,0,2 π,((2 π)/50)}]]},{}]]],

PlotRange->{{-6,6},{-6,6},{.5,6}},PlotRangePadding->.5,Boxed->False,
ViewAngle->.283,ViewCenter->{{.5,.5,.5},{.5,.6}}
]
],
{color,LightBlue},
Delimiter,
"Direction",
{{θ,0},0,2 π},{{ρ,(π/4)},0,π},
Delimiter,
{{show,True,"show light"},{False,True}},ControlPlacement->Left,
Initialization:>{
Clear[dirplane];
dirplane=
ParametricPlot3D[{x,y,0},{x,-4,4},{y,-4,4},Mesh->None,BoundaryStyle->Gray,PerformanceGoal->"Quality"]
}]

Point Light

Graphics3D[Sphere[],Lighting->{{"Point",White,{0,0,5}}}]

Point Light: Properties

Position matters

Manipulate[
With[{pt={xy[[1]],xy[[2]],z},center={0,0,0}},
Show[
Graphics3D[{
Specularity[White,50],
Sphere[{#[[1]],#[[2]],1}]&/@Tuples[{-2,2},2],
Dynamic[If[show,{color,Style[Sphere[pt,.4],Lighting->"Neutral"]},{}]],
Dynamic[If[env,{color,Opacity[.2],Style[Sphere[pt,5],Lighting->"Neutral"]},{}]]
},Lighting->{{"Point",color,pt}}],

plplane,

PlotRange->{{-5,5},{-5,5},{-7,7}},PlotRangePadding->.5,Boxed->boxed,Axes->axes
]
],
{color,Lighter@Orange},
Delimiter,
"Position",
{{xy,{0,0}},{-5,-5},{5,5}},{{z,6},-7,7},
Delimiter,
{{show,True,"show light"},{False,True}},
{{env,True,"show light envelope"},{False,True}},{{boxed,False,"boxed"},{False,True}},
{{axes,False,"axes"},{False,True}},ControlPlacement->Left,
Initialization:>{
Clear[plplane];
plplane=ParametricPlot3D[{x,y,0},{x,-5,5},{y,-5,5},
PlotPoints->40,
PlotStyle->Directive[Opacity[.75],Specularity[White,50]],Mesh->None,BoundaryStyle->Gray,PerformanceGoal->"Quality"]
}]

Point Light: Advanced Properties

Attenuation

Framed[Grid[{{"Formula:",Spacer[5],Style["surface intensity = \!\(\*FractionBox[\(\(light\)\(\\ \)\(intensity\)\(\\ \)\), \(a\\  + \\ b\\ x\\  + \\ c\\ \*SuperscriptBox[\(x\), \(2\)]\)]\)",Italic]},
{"","",""},
{"Syntax:","",Style["Lighting->{{\"Point\", color, position, {a,b,c}}}",FontFamily->"Courier",20,Bold]}
},BaseStyle->{FontFamily->"Times",FontSize->24},Alignment->{{Right,Center,Left},Center}],FrameStyle->GrayLevel[.75],FrameMargins->20]

Manipulate[Plot[1/(a+b x + c x^2),{x,0,10},PlotRange->{Full,{0,1}},PlotRangePadding->Scaled[.02],Epilog->Inset[Style["\!\(\*FractionBox[\(1\), \(a + b\\ x + c\\ \*SuperscriptBox[\(x\), \(2\)]\)]\)",14,Italic],Scaled[{.9,.8}],{Right,Top}]],{a,1,2},{b,0,1},{c,0,1}]

Point Light: Advanced Properties

Attenuation

Manipulate[
Show[

Graphics3D[Join[{White,Specularity[White,50]},planes,{Specularity[White,5],Glow@color,Sphere[{-2,5,5},.5],Lighting->"Neutral"}],Lighting->{{"Point",color,{-2,5,5},{a,b,c}}}],
Boxed->boxed,Axes->{axes,False,False},AxesLabel->{Style["x",20,Italic,FontFamily->"Times"],False,False},ViewPoint->{-1.5,-2.5,1}
],
{color,Lighter@Orange},
Delimiter,
"attenuation coefficents",
{{a,1,Style["a",16,FontFamily->"Times",Italic]},0,1},
{{b,0,Style["b",16,FontFamily->"Times",Italic]},0,2},
{{c,0,Style["c",16,FontFamily->"Times",Italic]},0,2},
Delimiter,
{{boxed,True,"boxed"},{False,True}},
{{axes,True,"axes"},{False,True}},
ControlPlacement->Left,
Initialization:>(planes=Table[Polygon[{{18-2i, i/2, i/2},{18-2i,10- i/2, i/2},{18-2i,10-i/2,10-i/2},{18-2i,i/2,10-i/2}}],{i,9,0,-1}])]

Spotlight

Graphics3D[Sphere[],Lighting->{{"Spot",White,{{0,0,5},{0,0,0}},(π/20)}}]

Manipulate[Graphics3D[Sphere[],Lighting->{{"Spot",White,{{0,0,5},{0,0,0}},θ}}],{θ,(π/40),(π/4)}]

Spotlight: Properties

Positions and cut-off angle

Manipulate[
With[{
pt={xy[[1]],xy[[2]],z},center={0,0,0},v=Normalize[{xy[[1]],xy[[2]],z}]},
Show[
Graphics3D[{
Specularity[White,50],
Sphere[{#[[1]],#[[2]],1}]&/@Tuples[{-2,2},2],

Dynamic[If[show,{EdgeForm[],Style[{color,Sphere[pt,.49],
GrayLevel[.25],
{CapForm[None],Tube[{pt+.5v,pt-.5v},.5],
CapForm["Butt"],Tube[{pt+.5v,pt+.51v},.5]}},Lighting->"Neutral"]},{}]],

Dynamic[If[env,{color,Opacity[.25],EdgeForm[],
Style[Cone[{center-10v,pt},Norm[center-10v-pt]Tan[θ]],Lighting->"Neutral"]},{}]]

},Lighting->{{"Spot",color,{pt,center},θ}}],

spotplane,

PlotRange->{{-5,5},{-5,5},{0,10}},PlotRangePadding->.75,Boxed->boxed,Axes->axes,
ViewPoint->{1.3,-3,1}
]
],
{color,Lighter@Yellow},
Delimiter,
"Position",
{{xy,{0,0}},{-5,-5},{5,5}},{{z,8},0,10},
Delimiter,
"Cut-off",
{{θ,(π/6),"half-angle"},0,(π/3)},
Delimiter,
{{show,True,"show light"},{False,True}},
{{env,True,"show light envelope"},{False,True}},{{boxed,False,"boxed"},{False,True}},
{{axes,False,"axes"},{False,True}},ControlPlacement->Left,
Initialization:>{
Clear[spotplane];
spotplane=ParametricPlot3D[{x,y,0},{x,-5,5},{y,-5,5},
PlotPoints->80,
PlotStyle->Specularity[White,50],Mesh->None,PerformanceGoal->"Speed"]
}]

Spotlight: Advanced Properties

Spot exponent

Framed[Grid[{{"Formula:",Spacer[5],"(cos^s)θ"},
{"","",""},
{"Syntax:","",Style["Lighting->{{\"Spot\", color, position, {θ, s}}}",FontFamily->"Courier",20,Bold]},
{"","",Style["Lighting->{{\"Spot\", color, {pos1, pos2}, {θ, s}}}",FontFamily->"Courier",20,Bold]}
},BaseStyle->{FontFamily->"Times",FontSize->24},Alignment->{{Right,Center,Left},Center}],FrameStyle->GrayLevel[.75],FrameMargins->20]

Manipulate[Plot[((Cos[t])^s),{t,0,Pi/2},PlotRange->{Full,{0,1}},PlotRangePadding->Scaled[.02],Epilog->Inset[Style["(cos^s)θ",20],Scaled[{.9,.8}],{Right,Top}]],{s,1,30}]

Spotlight: Advanced Properties

Spot exponent

Manipulate[
With[{pt={xy[[1]],xy[[2]],z},center={0,0,0}},
Show[
Graphics3D[{
Specularity[White,spec],
Sphere[{#[[1]],#[[2]],1}]&/@Tuples[{-2,2},2],
Dynamic[If[show,{EdgeForm[],Style[{color,Sphere[pt,.49],
GrayLevel[.25],
With[{v=Normalize[center-pt]},
{CapForm[None],Tube[{pt+.5v,pt-.5v},.5],
CapForm["Butt"],Tube[{pt-.5v,pt-.51v},.5]}]},Lighting->"Neutral"]},{}]]},Lighting->{{"Spot",color,{pt,center},{0,s}}}],

spotplane2[spec],

PlotRange->{{-5,5},{-5,5},{0,10}},PlotRangePadding->.75,Boxed->boxed,Axes->axes
]
],
{color,Lighter@Yellow},
Delimiter,
"Position",
{{xy,{0,0}},{-5,-5},{5,5}},{{z,8},0,10},
Delimiter,
{{s,10,"spot exponent"},1,30},
{{spec,20,"surface specularity"},1,50},
Delimiter,
{{show,True,"show light"},{False,True}},{{boxed,False,"boxed"},{False,True}},
{{axes,False,"axes"},{False,True}},ControlPlacement->Left,
Initialization:>{
Clear[spotplane2];
spotplane2[sp_]:=ParametricPlot3D[{x,y,0},{x,-5,5},{y,-5,5},
PlotPoints->80,PlotStyle->Specularity[White,spec],
Mesh->None,PerformanceGoal->"Speed"]}
]

Spotlight: Advanced Properties

Attenuation

Manipulate[
Graphics3D[
{
First@ParametricPlot3D[
Table[{i,i r Cos[t],i r Sin[t]},{i,4,20,2}],{r,0,Tan[Pi/10]},{t,0,2 Pi},
Mesh->None,
PlotPoints->25,
PlotStyle->{color},
Lighting->{{"Spot",color,{0,0,0},Pi/10,{a,b,c}}}],
Specularity[White,50],
{
EdgeForm[],
Style[{color,Sphere[{0,0,0},.98],
GrayLevel[.25],
CapForm[None],Tube[{{1,0,0},{-1,0,0}},1],
CapForm["Butt"],Tube[{{-1,0,0},{-1.1,0,0}},1]},Lighting->"Neutral"]
},
{Style[{Yellow,EdgeForm[],Specularity[White,30],Opacity[.2],Cone[{{22,0,0},{0,0,0}},22 Tan[Pi/10]]},Lighting->"Neutral"]}},Lighting->{{"Spot",color,{0,0,0},Pi/10,{a,b,c}}},
Boxed->boxed,
Axes->{axes,False,False},
ViewPoint->{-1.5,-2.5,1}],
{color,Lighter@Yellow},
Delimiter,
"attenuation coefficents",
{{a,1,Style["a",16,FontFamily->"Times",Italic]},0,1},
{{b,0,Style["b",16,FontFamily->"Times",Italic]},0,2},
{{c,0,Style["c",16,FontFamily->"Times",Italic]},0,2},
Delimiter,
{{boxed,True,"boxed"},{False,True}},
{{axes,True,"axes"},{False,True}},
ControlPlacement->Left,
Initialization:>(θ=Pi/10)]

Camera Related Options

ViewPoint

ViewAngle

ViewCenter

ViewVertical

ViewRange

ViewVector

ViewMatrix

Camera Related Options

Clear[billboard3D];
billboard3D[s_,width_,{x_,y_,z_},{nx_,ny_,nz_},{hx_,hy_,hz_}]:=
Module[{img=Rasterize[s,"Image",Background->None],height},
height=width ImageAspectRatio[img];
{
FaceForm[White],EdgeForm[],
Texture[ImageData[img]],
GeometricTransformation[Polygon[{{-.5width,.5height,0},{.5width,.5height,0},{.5width,-.5height,0},{-.5width,-.5height,0}},VertexTextureCoordinates->{{0,1},{1,1},{1,0},{0,0}}],
Quiet@Composition[TranslationTransform[{x,y,z}],RotationTransform[{{0,1,0},{hx,hy,hz}}],RotationTransform[{{0,0,1},{nx,ny,nz}}]]]
}
];

Framed[
Graphics3D[{
(* View frustum *)
FaceForm[],EdgeForm[GrayLevel[.25]],Cuboid[],

(* Objects *)
EdgeForm[],Specularity[White,10],
FaceForm[Red],Sphere[{.3,.4,.2},.2],
FaceForm[Blue],Cylinder[{{.5,.8,0},{.5,.8,.5}},.1],
FaceForm[Green],Cone[{{.7,.5,0},{.7,.5,.4}},.2],

(* Camera *)
FaceForm[GrayLevel[.25]],EdgeForm[],Specularity[White,30],
Cuboid[{.45,-1,.4},{.55,-1.3,.6}],
Cylinder[{{.475,-1.1,.65},{.525,-1.1,.65}},.05],
Cylinder[{{.475,-1.2,.65},{.525,-1.2,.65}},.05],
Cylinder[{{.5,-1,.5},{.5,-.9,.5}},.05],
Cylinder[{{.5,-1.15,.5},{.5,-.9,0}},.01],
Cylinder[{{.5,-1.15,.5},{.35,-1.3,0}},.01],
Cylinder[{{.5,-1.15,.5},{.65,-1.3,0}},.01],
billboard3D[Style["ViewPoint",24,Bold,FontFamily->"Courier"],.4,{.5,-1.15,.8},{1,0,0},{0,0,1}],

(* View span *)
GrayLevel[.25],
With[{p={.5,-.9,.5}},Line[{{p,{0,0,0}},{p,{1,0,0}},{p,{0,0,1}},{p,{1,0,1}}}]],

(* View angle *)
Gray,Dashed,
With[{p={.5,-.9,.5}},Line[{{p,{.5,0,1},{.5,0,0},p}}]],
Arrowheads[{-.015,.015}],Arrow[BezierCurve[{{.5,-.5,0.28},{.5,-.4,.5},{.5,-.5,.72}}]],
billboard3D[Style["ViewAngle",24,Bold,FontFamily->"Courier"],.3,{.5,-.4,.45},{1,0,0},{0,0,1}],

(* View vertical *)
billboard3D[Style["ViewVertical",24,Bold,FontFamily->"Courier"],.35,{.5,-.05,.75},{1,0,0},{0,-1,0}],

(* View center *)
PointSize[Large],Point[{{.5,.5,.5},{.5,0,.5}}],
Arrowheads[.015],Arrow[{{.5,.5,.5},{.5,0,.5}}],
billboard3D[Style["ViewCenter",24,Bold,FontFamily->"Courier"],.25,{.5,.25,.55},{1,0,0},{0,0,1}],

(* View range *)
Gray,
Line[{{{1,0,0},{1,0,-.1}},{{1,1,0},{1,1,-.1}}}],
Arrowheads[{-.02,.02}],Arrow[{{1,0,-0.05},{1,1,-.05}}],
billboard3D[Style["ViewRange",24,Bold,FontFamily->"Courier"],.4,{1.075,.5,-.1},{1,0,0},{0,0,1}]

},
Boxed->False,ImageSize->750,Lighting->"Neutral",
ViewPoint->{5,-2,1.5}
],
FrameMargins->20,FrameStyle->GrayLevel[.75]]

View Point, View Angle, and View Vertical

Manipulate[
With[{pt={Cos[θ]Cos[ρ],Sin[θ]Cos[ρ],Sin[ρ]}},
Framed[
Graphics3D[{
(* Objects *)
EdgeForm[],Specularity[White,10],
FaceForm[Red],Sphere[{-0.2,-0.1,-0.3},.2],
FaceForm[Blue],Cylinder[{{0.,0.3,-.5},{0.,0.3,0.}},.1],
FaceForm[Green],Cone[{{0.2,0.,-0.5},{0.2,0.,-0.1}},.2]
},
PlotRange->.5,
RotationAction->"Clip",
Boxed->True,Lighting->"Neutral",
ViewPoint->r pt,
ViewAngle->ϕ,
ViewVertical->RotationTransform[τ,-pt,r pt][{Cos[θ](-Sin[ρ]),Sin[θ](-Sin[ρ]),Cos[ρ]}+r pt],
Axes->True,Ticks->False,
AxesLabel->{"x","y","z"},BaseStyle->{FontSize->12}
],
FrameMargins->0,FrameStyle->LightGray]
],
Dynamic[Row[{Style["view point: ",Bold],Chop[NumberForm[#,3]]&/@(r{Cos[θ]Cos[ρ],Sin[θ]Cos[ρ],Sin[ρ]})}]],
{{θ,N@4Pi/3},0,2Pi},
{{ρ,N@Pi/10},-Pi/2+.1,Pi/2-.1},
{{r,3},1,10},
ActionMenu["view from",{
"Default":>(θ=N@4Pi/3;ρ=N@Pi/10),
Delimiter,
"Front":>(θ=N@3Pi/2;ρ=0.),
"Back":>(θ=N@Pi/2;ρ=0.),
"Right":>(θ=0.;ρ=0.),
"Back":>(θ=N@Pi;ρ=0.),
"Top":>(θ=N@3Pi/2;ρ=N[Pi/2-.1]),"Bottom":>(θ=N@3Pi/2;ρ=N[-Pi/2+.1])}],
Delimiter,
Style["view angle",Bold],
{{ϕ,N[35 Degree]},Pi/20,Pi/2},
{{ϕ,N[35 Degree],""},{Automatic,All},ControlType->RadioButton},
Delimiter,
Dynamic[
With[{pt={Cos[θ]Cos[ρ],Sin[θ]Cos[ρ],Sin[ρ]}},Row[{Style["view vertical: ",Bold],Chop[NumberForm[#,3]]&/@(RotationTransform[τ,-pt,r pt][{Cos[θ](-Sin[ρ]),Sin[θ](-Sin[ρ]),Cos[ρ]}+r pt])}]]],
{{τ,0},0,2Pi},
Dynamic[
With[{pt={Cos[θ]Cos[ρ],Sin[θ]Cos[ρ],Sin[ρ]}},
Graphics3D[{{
EdgeForm[],Specularity[White,10],
FaceForm[Red],Sphere[{-0.2,-0.1,-0.3},.2],
FaceForm[Blue],Cylinder[{{0.,0.3,-.5},{0.,0.3,0.}},.1],
FaceForm[Green],Cone[{{0.2,0.,-0.5},{0.2,0.,-0.1}},.2],
FaceForm[],EdgeForm[Gray],Cuboid[{-.5,-.5,-.5},{.5,.5,.5}],
FaceForm[White],Opacity[.1],Sphere[{0,0,0},r],
Black,Opacity[1],
Cylinder[{r pt,(r+.2)pt},.1],
Arrowheads[.02],
Arrow[{r pt,RotationTransform[τ,-pt,r pt][.2r{Cos[θ](-Sin[ρ]),Sin[θ](-Sin[ρ]),Cos[ρ]}+r pt]}],
White,Opacity[.2],
If[NumberQ[N@ϕ],Cone[{{0,0,0},r pt},Norm[r pt]Tan[ϕ/2]],{}],
Dotted,Gray,Opacity[1],Line[N@Table[r{Cos[t]Cos[ρ],Sin[t]Cos[ρ],Sin[ρ]},{t,0,2 π,((2 π)/50)}]],
Line[N@Table[r{Cos[θ]Cos[s],Sin[θ]Cos[s],Sin[s]},{s,0,2 π,((2 π)/50)}]]
}},ViewPoint->Front,Boxed->False,Lighting->"Neutral",ImageSize->250,SphericalRegion->True]
]
],
ControlPlacement->Left]

Interactive View Control

Graphics3D[{
(* Objects *)
EdgeForm[],Specularity[White,10],
FaceForm[Red],Sphere[{-0.2,-0.1,-0.3},.2],
FaceForm[Blue],Cylinder[{{0.,0.3,-.5},{0.,0.3,0.}},.1],
FaceForm[Green],Cone[{{0.2,0.,-0.5},{0.2,0.,-0.1}},.2]
},
Boxed->True,Lighting->"Neutral"
]

Dynamic View Options

DynamicModule[
{point={1.3,-2.4,2},angle=N[35Degree],vertical={0,0,1},center=Automatic},
Grid[{{
Framed[
Graphics3D[{
(* Objects *)
EdgeForm[],Specularity[White,20],
FaceForm[Red],Sphere[{-0.2,-0.1,-0.3},.2],
FaceForm[Blue],Cylinder[{{0.,0.3,-.5},{0.,0.3,0.}},.1],
FaceForm[Green],Cone[{{0.2,0.,-0.5},{0.2,0.,-0.1}},.2]
},
Boxed->True,Lighting->"Neutral",
ImageSize->450, RotationAction->"Clip",

(* View control *)
ViewPoint->Dynamic[point],
ViewAngle->Dynamic[angle],
ViewVertical->Dynamic[vertical],
ViewCenter->Dynamic[center]
],
FrameStyle->LightGray],

(* Display option values *)
Grid[{
{Style["view point: ",Bold],Dynamic[point]},
{Style["view angle: ",Bold],Dynamic[angle]},
{Style["view vertical: ",Bold],Dynamic[vertical]},
{Style["view center: ",Bold],Dynamic[center]}
},Alignment->{{Right,Left},Center},
BaseStyle->{"Label",FontSize->16}]
}}]
]

DynamicModule[
{point={1.3,-2.4,2},angle=N[35Degree],vertical={0,0,1},center=Automatic},
Grid[{{
Framed[
Graphics3D[{
(* Objects *)
EdgeForm[],Specularity[White,20],
FaceForm[Red],Sphere[{-0.2,-0.1,-0.3},.2],
FaceForm[Blue],Cylinder[{{0.,0.3,-.5},{0.,0.3,0.}},.1],
FaceForm[Green],Cone[{{0.2,0.,-0.5},{0.2,0.,-0.1}},.2]
},
Boxed->True,Lighting->"Neutral",
ImageSize->300, RotationAction->"Clip",

(* View control *)
ViewPoint->Dynamic[point],
ViewAngle->Dynamic[angle],
ViewVertical->Dynamic[vertical],
ViewCenter->Dynamic[center]
],
FrameStyle->LightGray],

(* The second object *)
Framed[
Plot3D[Sin[x y],{x,0,3},{y,0,3},
ImageSize->300,Axes->False,
(* View control *)
ViewPoint->Dynamic[point],
ViewAngle->Dynamic[angle],
ViewVertical->Dynamic[vertical],
ViewCenter->Dynamic[center]
],
FrameStyle->LightGray]
}}]
]

Surface Properties

Glowing color: Glow

Diffuse reflection: Colors, FaceForm

Specular reflection: Specularity

Opacity

Texture

Colors and Reflections

Manipulate[
With[{directive=Directive[FaceForm[Directive[Glow[glow],fcolor,Specularity[speccolor,specexpo],Opacity[opacity]],Directive[Glow[bglow],bcolor,Specularity[bspeccolor,bspecexpo],Opacity[bopacity]]]]},
Show[
Switch[object,
"plot",Plot3D[Sin[x y],{x,0,3},{y,0,3},Mesh->None,ImageSize->300,Axes->False,PlotStyle->directive,PerformanceGoal->"Quality"],
"sphere",Graphics3D[{directive,Sphere[]}],
"cube",Graphics3D[{directive,Cuboid[]}],
"tube",Graphics3D[{directive,CapForm[None],Tube[{{0,0,0},{0,0,1}},.5]}]
],
PlotRange->{All,All,{All,z}},PlotRangePadding->Scaled[.02],
Lighting->lighting,Boxed->boxed,Background->bg]
],

{object,{"plot","sphere","cube","tube"}},
{{z,1,"height"},.5,1},
Delimiter,

TabView[{
"front"->
Grid[{
{"glow",Control@{{glow,Black,""},Black}},
{Style["\ndiffuse",Bold],""},
{"color",Control@{{fcolor,Orange,""},Orange}},
{"opacity",Control@{{opacity,1,""},0,1}},
{Style["\nspecular",Bold],""},
{"color",Control@{{speccolor,Black,""},Black}},
{"exponent",Control@{{specexpo,10,""},0,50}}
},Alignment->{{Right,Left},Center},Spacings->{0,0}],

"back"->
Grid[{
{"glow",Control@{{bglow,Black,""},Black}},
{Style["\ndiffuse",Bold],""},
{"color",Control@{{bcolor,Blue,""},Blue}},
{"opacity",Control@{{bopacity,1,""},0,1}},
{Style["\nspecular",Bold],""},
{"color",Control@{{bspeccolor,Black,""},Black}},
{"exponent",Control@{{bspecexpo,10,""},0,50}}
},Alignment->{{Right,Left},Center},Spacings->{0,0}]
}],
Delimiter,
{{lighting,"Neutral"},{"Neutral",Automatic},ControlType->RadioButton},
{boxed,{True,False}},
{{bg,White,"background"},White},
ControlPlacement->Left
]

Texture

Graphics3D[{Texture[],Polygon[{{-1,-1,0},{1,-1,0},{1,1,0},{-1,1,0}},VertexTextureCoordinates->{{0,0},{1,0},{1,1},{0,1}}]},Lighting->"Neutral"]

ParametricPlot3D[{(3+Cos[v])Cos[u],(3+Cos[v])Sin[u],Sin[v]},{u,0,2Pi},{v,0,2Pi},Mesh->None,PlotStyle->Texture[],Lighting->"Neutral"]

Texture with Everything

Plot on a plot:

contour=ContourPlot[Sin[x y],{x,0,3},{y,0,3},ColorFunction->"Rainbow",Frame->False,Axes->False,PlotRangePadding->None]

Plot3D[Sin[x y],{x,0,3},{y,0,3},PlotStyle->Texture[contour],Mesh->None,Lighting->"Neutral"]

Any expression:

Graphics3D[{Texture[Style["(∫_0^x)(1/(Exp[t]))\[DifferentialD]t",120]],Polygon[{{-2,-1,0},{2,-1,0},{2,1,0},{-2,1,0}},VertexTextureCoordinates->{{0,0},{1,0},{1,1},{0,1}}]},Lighting->"Neutral"]

Dynamic texture:

Graphics3D[{Dynamic[Texture[CurrentImage[]]],Polygon[{{-1.6,-1,0},{1.6,-1,0},{1.6,1,0},{-1.6,1,0}},VertexTextureCoordinates->{{0,0},{1,0},{1,1},{0,1}}]},Lighting->"Neutral"]

Graphics3D[{
Specularity[White,20],Dynamic[Texture[CurrentImage[]]],EdgeForm[],(Append[#1,{VertexTextureCoordinates->With[{n=Length[First[#1]]},
Table[(1/2) {Cos[((2 π i)/n)],Sin[((2 π i)/n)]}+{(1/2),(1/2)},{i,0,n-1}]]}]&)/@Flatten[Normal[PolyhedronData["RhombicHexecontahedron","Faces","Polygon"]]]},
Lighting->"Neutral"]

Texture Coordinates for Vertices

DynamicModule[{pts=Table[.5{Cos[((2 π n)/8)],Sin[((2 π n)/8)]}+{.5,.5},{n,0,7}],monalisa=},
Grid[{
{"Texture","Polygon"},
{
LocatorPane[Dynamic[pts],Graphics[{Inset[monalisa,{0,0},{Left,Bottom},{1,1}],FaceForm[],EdgeForm[Directive[Orange,Thick]],Polygon[Dynamic[pts]],Dynamic@MapIndexed[Text[Style[#2[[1]],White,12],.9(#1-{.5,.5})+{.5,.5}]&,pts]},PlotRange->{{0,1},{0,1}},ImageSize->300]],
Graphics3D[{Texture[monalisa],Polygon[{#[[1]],#[[2]],0}&/@pts,VertexTextureCoordinates->Dynamic[pts]],MapIndexed[Text[Style[#2[[1]],12],Append[1.1(#1-{.5,.5})+{.5,.5},0]]&,pts]},ImageSize->300,Lighting->"Neutral",PlotRange->{All,All,{-.25,.25}}]
},
{Button["Reset",pts=Table[.5{Cos[((2 π n)/8)],Sin[((2 π n)/8)]}+{.5,.5},{n,0,7}],ImageSize->100],""}
},BaseStyle->{FontFamily->"Arial"}]
]

Double-Sided Texture

Graphics3D[{FaceForm[Texture[],Texture[]],Polygon[{{-1,-1,0},{1,-1,0},{1,1,0},{-1,1,0}},VertexTextureCoordinates->{{0,0},{1,0},{1,1},{0,1}}]},Lighting->"Neutral"]

Built-In Textures

ExampleData—“Texture”

ExampleData[{"Texture","Bark"}]

Grid[Partition[Labeled[ImageResize[ExampleData[#],64],Style[#[[2]],10,FontFamily->"Arial"]]&/@ExampleData["Texture"],8],Spacings->{.5,.5}]

ExampleData—”ColorTexture”

ExampleData[{"ColorTexture","Ash"}]

Grid[Partition[Labeled[ImageResize[ExampleData[#],64],Style[#[[2]],10,FontFamily->"Arial"]]&/@ExampleData["ColorTexture"],8],Spacings->{.5,.5}]

Using Multi-Object Syntax

Many primitives support multi-object.

g=Graphics3D[Table[Point[{i,0,0}],{i,1000}]]

mg=Graphics3D[Point[Table[{i,0,0},{i,1000}]]]

ByteCount[g]

ByteCount[mg]

Point, Line, Polygon, Tube, Sphere, ....

Refactoring Objects

GraphicsComplex makes it possible to use indices instead of coordinates.

Graphics3D[{Polygon[{{0,0,0},{2,0,0},{1,1,2}}],Polygon[{{2,0,0},{2,2,0},{1,1,2}}],Polygon[{{2,2,0},{0,2,0},{1,1,2}}],Polygon[{{0,2,0},{0,0,0},{1,1,2}}]}]

vertices={{0,0,0},{2,0,0},{2,2,0},{0,2,0},{1,1,2}};

indices={{1,2,5},{2,3,5},{3,4,5},{4,1,5}};

Graphics3D[GraphicsComplex[vertices,Polygon[indices]]]

Constructing GraphicsComplex from PolyhedronData.

vtx=PolyhedronData["Dodecahedron","VertexCoordinates"];

idx=PolyhedronData["Dodecahedron","FaceIndices"];

gc=GraphicsComplex[vtx,Polygon[idx]];

Graphics3D[gc]

Normal undoes GraphicsComplex.

ngc=Normal[GraphicsComplex[vtx,Polygon[idx]]];

Graphics3D[ngc]

ByteCount[gc]

ByteCount[ngc]

N[ByteCount[ngc]/ByteCount[gc]]

Cloning Objects

obj=ExampleData[{"Geometry3D","Beethoven"},"GraphicsComplex"];

vtx=obj[[1]];
poly=obj[[2]];

g1=Graphics3D[{EdgeForm[],Table[GraphicsComplex[#+12.{i,j,k}&/@vtx,poly],{i,3},{j,3},{k,3}]}]

GeometricTransform can multiply an object.

g2=Graphics3D[{EdgeForm[],GeometricTransformation[obj,Flatten[Table[TranslationTransform[12.{i,j,k}],{i,3},{j,3},{k,3}]]]}]

ByteCount[g1]

ByteCount[g2]

N[ByteCount[g1]/ByteCount[g2]]

Numericizing and Packing If Possible

How the Front End handles non-machine numbers: ToBoxes.

Graphics3D[Line[{{(1/2),(1/3),((Sqrt[5])/2)},{(10/3),1.5,π}}]]

ToBoxes[%]

Numericizing makes difference: N.

g=Graphics3D[BSplineCurve[Table[{Cos[θ],Sin[θ],(θ/4)},{θ,0,2 π,((2 π)/20)}]]]

ByteCount[g]

ng=Graphics3D[BSplineCurve[N[Table[{Cos[θ],Sin[θ],(θ/4)},{θ,0,2 π,((4 π)/20)}]]]]

ByteCount[ng]

Explicit packing helps: Developer`ToPackedArray.

coords=Table[{Cos[θ],Sin[θ],(θ/4)},{θ,0,2 π,((4 π)/20)}]

ByteCount[coords]

ncoords=N[Table[{Cos[θ],Sin[θ],(θ/4)},{θ,0,2 π,((4 π)/20)}]];

ByteCount[ncoords]

Developer`PackedArrayQ[ncoords]

pcoords=Developer`ToPackedArray[ncoords];

ByteCount[pcoords]

Developer`PackedArrayQ[pcoords]

Localizing Dynamics

Many Graphics3D primitives, directives, and option values can take Dynamic.

DynamicModule[{pt={0,0}},
{Graphics3D[
Cylinder[{{0,0,0},Dynamic[{pt[[1]],pt[[2]],2}]},1]],Slider2D[Dynamic[pt],{{-1,-1},{1,1}}]}]

DynamicModule[{color=Red},{Graphics3D[{Dynamic[color],Sphere[]}],ColorSlider[Dynamic[color]]}]

DynamicModule[{z=1},{Graphics3D[Sphere[],PlotRange->Dynamic[{All,All,{-1,z}}]],Slider[Dynamic[z],{-1,1}]}]

Inefficient Dynamic:

DynamicModule[{z=0},{
Dynamic[
ParametricPlot3D[{(1.16^v) Cos[v] (1+Cos[u]),-(1.16^v) Sin[v] (1+Cos[u]),-2 (1.16^v) (1+Sin[u])},{u,0,2 π},{v,-15,6},
Mesh->None,Axes->True,MaxRecursion->4,ImageSize->300,
PlotRange->{All,All,{-10,z}}]
],
VerticalSlider[Dynamic[z],{-9,0}]
}]

Efficient Dynamic—minimizing reevaluation

DynamicModule[{z=0},{
Show[
ParametricPlot3D[{(1.16^v) Cos[v] (1+Cos[u]),-(1.16^v) Sin[v] (1+Cos[u]),-2 (1.16^v) (1+Sin[u])},{u,0,2 π},{v,-15,6},
Mesh->None,Axes->True,MaxRecursion->4,ImageSize->300],
PlotRange->Dynamic[{All,All,{-10,z}}]],
VerticalSlider[Dynamic[z],{-9,0}]
}]