advanced.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #!/usr/bin/env python
  2. # Author: Kwasi Mensah
  3. # Date: 7/11/2005
  4. #
  5. # This is a tutorial to show some of the more advanced things
  6. # you can do with Cg. Specifically, with Non Photo Realistic
  7. # effects like Toon Shading. It also shows how to implement
  8. # multiple buffers in Panda.
  9. from direct.showbase.ShowBase import ShowBase
  10. from panda3d.core import PandaNode, LightNode, TextNode
  11. from panda3d.core import Filename
  12. from panda3d.core import NodePath
  13. from panda3d.core import Shader
  14. from panda3d.core import LVecBase4
  15. from direct.task.Task import Task
  16. from direct.actor.Actor import Actor
  17. from direct.gui.OnscreenText import OnscreenText
  18. from direct.showbase.DirectObject import DirectObject
  19. from direct.showbase.BufferViewer import BufferViewer
  20. import sys
  21. import os
  22. # Function to put instructions on the screen.
  23. def addInstructions(pos, msg):
  24. return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1),
  25. parent=base.a2dTopLeft, align=TextNode.ALeft,
  26. pos=(0.08, -pos - 0.04), scale=.05)
  27. # Function to put title on the screen.
  28. def addTitle(text):
  29. return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08,
  30. parent=base.a2dBottomRight, align=TextNode.ARight,
  31. fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))
  32. class ToonMaker(ShowBase):
  33. def __init__(self):
  34. # Initialize the ShowBase class from which we inherit, which will
  35. # create a window and set up everything we need for rendering into it.
  36. ShowBase.__init__(self)
  37. self.disableMouse()
  38. camera.setPos(0, -50, 0)
  39. # Check video card capabilities.
  40. if not self.win.getGsg().getSupportsBasicShaders():
  41. addTitle("Toon Shader: Video driver reports that Cg shaders are not supported.")
  42. return
  43. # Show instructions in the corner of the window.
  44. self.title = addTitle(
  45. "Panda3D: Tutorial - Toon Shading with Normals-Based Inking")
  46. self.inst1 = addInstructions(0.06, "ESC: Quit")
  47. self.inst2 = addInstructions(0.12, "Up/Down: Increase/Decrease Line Thickness")
  48. self.inst3 = addInstructions(0.18, "Left/Right: Decrease/Increase Line Darkness")
  49. self.inst4 = addInstructions(0.24, "V: View the render-to-texture results")
  50. # This shader's job is to render the model with discrete lighting
  51. # levels. The lighting calculations built into the shader assume
  52. # a single nonattenuating point light.
  53. tempnode = NodePath(PandaNode("temp node"))
  54. tempnode.setShader(loader.loadShader("lightingGen.sha"))
  55. self.cam.node().setInitialState(tempnode.getState())
  56. # This is the object that represents the single "light", as far
  57. # the shader is concerned. It's not a real Panda3D LightNode, but
  58. # the shader doesn't care about that.
  59. light = render.attachNewNode("light")
  60. light.setPos(30, -50, 0)
  61. # this call puts the light's nodepath into the render state.
  62. # this enables the shader to access this light by name.
  63. render.setShaderInput("light", light)
  64. # The "normals buffer" will contain a picture of the model colorized
  65. # so that the color of the model is a representation of the model's
  66. # normal at that point.
  67. normalsBuffer = self.win.makeTextureBuffer("normalsBuffer", 0, 0)
  68. normalsBuffer.setClearColor(LVecBase4(0.5, 0.5, 0.5, 1))
  69. self.normalsBuffer = normalsBuffer
  70. normalsCamera = self.makeCamera(
  71. normalsBuffer, lens=self.cam.node().getLens())
  72. normalsCamera.node().setScene(render)
  73. tempnode = NodePath(PandaNode("temp node"))
  74. tempnode.setShader(loader.loadShader("normalGen.sha"))
  75. normalsCamera.node().setInitialState(tempnode.getState())
  76. # what we actually do to put edges on screen is apply them as a texture to
  77. # a transparent screen-fitted card
  78. drawnScene = normalsBuffer.getTextureCard()
  79. drawnScene.setTransparency(1)
  80. drawnScene.setColor(1, 1, 1, 0)
  81. drawnScene.reparentTo(render2d)
  82. self.drawnScene = drawnScene
  83. # this shader accepts, as input, the picture from the normals buffer.
  84. # it compares each adjacent pixel, looking for discontinuities.
  85. # wherever a discontinuity exists, it emits black ink.
  86. self.separation = 0.001
  87. self.cutoff = 0.3
  88. inkGen = loader.loadShader("inkGen.sha")
  89. drawnScene.setShader(inkGen)
  90. drawnScene.setShaderInput("separation", LVecBase4(self.separation, 0, self.separation, 0))
  91. drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff))
  92. # Panda contains a built-in viewer that lets you view the results of
  93. # your render-to-texture operations. This code configures the viewer.
  94. self.accept("v", self.bufferViewer.toggleEnable)
  95. self.accept("V", self.bufferViewer.toggleEnable)
  96. self.bufferViewer.setPosition("llcorner")
  97. # Load a dragon model and start its animation.
  98. self.character = Actor()
  99. self.character.loadModel('models/nik-dragon')
  100. self.character.reparentTo(render)
  101. self.character.loop('win')
  102. self.character.hprInterval(15, (360, 0, 0)).loop()
  103. # These allow you to change cartooning parameters in realtime
  104. self.accept("escape", sys.exit, [0])
  105. self.accept("arrow_up", self.increaseSeparation)
  106. self.accept("arrow_down", self.decreaseSeparation)
  107. self.accept("arrow_left", self.increaseCutoff)
  108. self.accept("arrow_right", self.decreaseCutoff)
  109. def increaseSeparation(self):
  110. self.separation = self.separation * 1.11111111
  111. print("separation: %f" % (self.separation))
  112. self.drawnScene.setShaderInput(
  113. "separation", LVecBase4(self.separation, 0, self.separation, 0))
  114. def decreaseSeparation(self):
  115. self.separation = self.separation * 0.90000000
  116. print("separation: %f" % (self.separation))
  117. self.drawnScene.setShaderInput(
  118. "separation", LVecBase4(self.separation, 0, self.separation, 0))
  119. def increaseCutoff(self):
  120. self.cutoff = self.cutoff * 1.11111111
  121. print("cutoff: %f" % (self.cutoff))
  122. self.drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff))
  123. def decreaseCutoff(self):
  124. self.cutoff = self.cutoff * 0.90000000
  125. print("cutoff: %f" % (self.cutoff))
  126. self.drawnScene.setShaderInput("cutoff", LVecBase4(self.cutoff))
  127. t = ToonMaker()
  128. t.run()