Skip to content

Latest commit

 

History

History
540 lines (455 loc) · 18.1 KB

File metadata and controls

540 lines (455 loc) · 18.1 KB
Error in user YAML: (<unknown>): found a tab character that violate indentation while scanning a plain scalar at line 3 column 3
---
- oeasy Python 0746
- 这是 oeasy 系统化 Python 教程,从基础一步步讲,扎实、完整、不跳步。愿意花时间学,就能真正学会。
- 本教程同步发布在: 
	- 个人网站: `https://oeasy.org` 
	- 蓝桥云课: `https://www.lanqiao.cn/courses/3584` 
	- GitHub: `https://github.com/overmind1980/oeasy-python-tutorial` 
	- Gitee: `https://gitee.com/overmind1980/oeasypython` 
---

发光

import bpy

def clear_scene():
    bpy.ops.object.select_all(action="SELECT")
    bpy.ops.object.delete()

def create_man():
    mat = bpy.data.materials.new('blue')
    color = (0, 0, 1, 1)
    mat.use_nodes = True
    mat.node_tree.nodes["Principled BSDF"].inputs['Base Color'].default_value = color
    man = bpy.data.objects.new("man", None)
    bpy.data.collections["Collection"].objects.link(man)
    bpy.ops.mesh.primitive_uv_sphere_add()
    head = bpy.context.object
    head.location = (0,0,2.5)
    head.data.materials.append(mat)
    head.parent = man
    bpy.ops.mesh.primitive_cone_add()
    body = bpy.context.object
    body.location = (0,0,1)
    body.parent = man
    body.data.materials.append(mat)

def create_plane():
    bpy.ops.mesh.primitive_plane_add()
    plane = bpy.context.object
    plane.scale = (10,10,1)

def create_camera():
    bpy.ops.object.camera_add()
    camera = bpy.context.object
    camera.location = (3.059990406036377, -9.885231018066406, 12.905241966247559)
    camera.rotation_euler = (0.7260572910308838, -6.679311326251991e-09, 0.30019673705101013)
    bpy.context.scene.camera = camera

def create_spot():
    bpy.ops.object.light_add(type='SPOT')
    spot = bpy.context.object
    spot.location = (-1.2811667919158936, -3.316868543624878, 12.282358169555664)
    spot.rotation_euler = (0.34208473563194275, 1.8850268901360323e-08, -0.3071775436401367)
    spot.data.energy = 1000

def render():
    bpy.context.scene.render.resolution_x = 320
    bpy.context.scene.render.resolution_y = 240
    bpy.context.scene.render.resolution_percentage = 50
    bpy.context.scene.render.engine = 'CYCLES'
    bpy.context.scene.render.filepath = '/tmp/render2.png'
    bpy.ops.render.render(write_still=True)

clear_scene()
create_man()
create_plane()
create_camera()
create_spot()
render()

制作小鸟

  • 蓝光超标
  • 有发光效果

图片描述

  • 可以得到小鸟
import bpy

def clear_scene():
    bpy.ops.object.select_all(action="SELECT")
    bpy.ops.object.delete()
    
def create_purple_bird():
    mat = bpy.data.materials.new('blue')
    mat.use_nodes = True
    color = (2.2, 2, 112, 1)
    mat.node_tree.nodes["Principled BSDF"].inputs['Base Color'].default_value = color
    bird = bpy.data.objects.new("bird", None)
    bpy.data.collections["Collection"].objects.link(bird)
    
    bpy.ops.mesh.primitive_uv_sphere_add()
    body=bpy.context.object
    body.scale = (1.3512587547302246, 1.6976544857025146, 1.477585792541504)
    body.location = (0.0, 0.0, 1.9078407287597656)
    body.rotation_euler = (0.33339381217956543, 0.8460186123847961, -1.282267689704895)
    body.data.materials.append(mat)
    body.parent = bird
    body.name = "body"
    
    bpy.ops.mesh.primitive_uv_sphere_add()
    head=bpy.context.object
    head.scale = (1.0995900630950928, 0.9614527821540833, 1.0065921545028687)
    head.location = (-0.7161910533905029, -0.06186628341674805, 2.9344260692596436)
    head.rotation_euler = (-0.9862819314002991, 1.3689559698104858, 0.865427553653717)
    head.data.materials.append(mat)
    head.parent = bird
    head.name = "head"

    bpy.ops.mesh.primitive_cone_add()
    mouth=bpy.context.object
    mouth.scale = (0.2851811349391937, 0.2851811349391937, 0.2851811945438385)
    mouth.location = (-1.718659520149231, 0.005130168981850147, 3.438387632369995)
    mouth.rotation_euler = (-0.9014008641242981, 0.8561369776725769, -4.252285957336426)
    mouth.data.materials.append(mat)
    mouth.parent = bird
    mouth.name = "mouth"
    
    bpy.ops.mesh.primitive_cone_add()
    tail=bpy.context.object
    tail.scale = (0.5862210392951965, 0.5862210392951965, 0.5862210392951965)
    tail.location = (1.4573625326156616, -0.1262585073709488, 2.4004876613616943)
    tail.rotation_euler = (-0.0, -1.7943094968795776, -0.0)
    tail.data.materials.append(mat)
    tail.parent = bird
    tail.name = "tail"
    
    bpy.ops.mesh.primitive_ico_sphere_add()
    wing1=bpy.context.object
    wing1.scale = (0.9348270893096924, 0.7513998746871948, 0.6549702882766724)
    wing1.location = (0.28463542461395264, -1.0785921812057495, 2.1733691692352295)
    wing1.rotation_euler = (-0.06378283351659775, -0.3458176255226135, 0.06053333729505539) 
    wing1.data.materials.append(mat)
    wing1.parent = bird
    wing1.name = "wing1"
    
    bpy.ops.mesh.primitive_ico_sphere_add()
    wing2=bpy.context.object
    wing2.scale = (0.9348270893096924, 0.7513998746871948, 0.6549702882766724)
    wing2.location = (0.28463542461395264, 1.1649233102798462, 2.1733691692352295)
    wing2.rotation_euler = (-0.06378283351659775, -0.3458176255226135, 0.06053333729505539) 
    wing2.data.materials.append(mat)
    wing2.parent = bird
    wing2.name = "wing2"

    bpy.ops.mesh.primitive_uv_sphere_add()
    eye1=bpy.context.object
    eye1.scale = (0.19664300978183746, 0.19664300978183746, 0.19664300978183746)
    eye1.location = (-0.9229257702827454, -0.7575194239616394, 3.5738308429718018)
    eye1.data.materials.append(mat)
    eye1.parent = bird
    eye1.name = "eye1"
     
    bpy.ops.mesh.primitive_uv_sphere_add()
    eye2=bpy.context.object
    eye2.scale = (0.19664300978183746, 0.19664300978183746, 0.19664300978183746)
    eye2.location = (-0.9229257702827454, 0.634847104549408, 3.5738308429718018)
    eye2.data.materials.append(mat)
    eye2.parent = bird
    eye2.name = "eye2"

    
    
def create_plane():
    bpy.ops.mesh.primitive_plane_add()
    plane = bpy.context.object
    plane.scale = (10.0, 10.0, 1.0)  

def create_camera():
    bpy.ops.object.camera_add()
    camera = bpy.context.object
    camera.location = (-0.1523854285478592, -12.848016738891602, 9.196365356445312)
    camera.rotation_euler = (1.135628342628479, 1.113783127948409e-07, -0.01396263763308525)
    bpy.context.scene.camera = camera
    
def create_spot():
    bpy.ops.object.light_add(type='SPOT')
    spot = bpy.context.object
    spot.location = (-12.789496421813965, 9.167668342590332, 8.237796783447266)
    spot.rotation_euler = (1.3590290546417236, 7.051307875372004e-07, -2.285219669342041)
    spot.data.energy = 1000
    
def render():
    bpy.context.scene.render.resolution_x = 320
    bpy.context.scene.render.resolution_y = 240
    bpy.context.scene.render.resolution_percentage = 50
    bpy.context.scene.render.engine = 'CYCLES'
    bpy.context.scene.render.filepath = '/tmp/o.png'
    bpy.ops.render.render(write_still=True)


clear_scene()
create_purple_bird()
create_plane()
create_camera()
create_spot()
render()
  • 材质半透明效果

图片描述

import bpy
import math
import os
from bpy_extras.io_utils import ExportHelper

# 清除场景原有物体
for obj in bpy.data.objects:
    bpy.data.objects.remove(obj)

# --------------------------
# 创建鱼模型
# --------------------------
# 鱼身主体
bpy.ops.mesh.primitive_uv_sphere_add(radius=1, location=(0, 0, 0))
body = bpy.context.active_object
body.name = "Fish_Body"
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.transform.resize(value=(3, 1, 0.8), orient_type='GLOBAL')
bpy.ops.transform.resize(value=(1, 1, 0.7), orient_type='GLOBAL')
bpy.ops.object.mode_set(mode='OBJECT')

# 鱼尾
bpy.ops.mesh.primitive_cube_add(size=1, location=(3.5, 0, 0))
tail = bpy.context.active_object
tail.name = "Fish_Tail"
tail.scale = (0.8, 1.2, 0.6)
tail.location = (3.2, 0, 0)

# 背鳍
bpy.ops.mesh.primitive_cube_add(size=1, location=(0.5, 0, 1))
dorsal_fin = bpy.context.active_object
dorsal_fin.name = "Dorsal_Fin"
dorsal_fin.scale = (1.5, 0.2, 0.8)
dorsal_fin.rotation_euler.z = math.radians(5)

# 胸鳍
bpy.ops.mesh.primitive_cube_add(size=1, location=(-0.5, -1, 0))
left_fin = bpy.context.active_object
left_fin.name = "Left_Fin"
left_fin.scale = (0.8, 0.2, 0.5)
left_fin.rotation_euler.x = math.radians(30)

bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 2, 0)})
right_fin = bpy.context.active_object
right_fin.name = "Right_Fin"

# 腹鳍
bpy.ops.mesh.primitive_cube_add(size=1, location=(1, 0, -0.8))
ventral_fin = bpy.context.active_object
ventral_fin.name = "Ventral_Fin"
ventral_fin.scale = (0.6, 0.2, 0.4)
ventral_fin.rotation_euler.x = math.radians(-20)

# 眼睛
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.3, location=(-1.5, -0.7, 0.3))
left_eye_white = bpy.context.active_object
left_eye_white.name = "Left_Eye_White"

bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 1.4, 0)})
right_eye_white = bpy.context.active_object
right_eye_white.name = "Right_Eye_White"

bpy.ops.mesh.primitive_uv_sphere_add(radius=0.15, location=(-1.6, -0.7, 0.3))
left_pupil = bpy.context.active_object
left_pupil.name = "Left_Pupil"
left_pupil.scale = (1, 0.8, 1)

bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 1.4, 0)})
right_pupil = bpy.context.active_object
right_pupil.name = "Right_Pupil"

# --------------------------
# 创建材质
# --------------------------
# 鱼身材质
body_mat = bpy.data.materials.new(name="Body_Material")
body_mat.use_nodes = True
nodes = body_mat.node_tree.nodes
links = body_mat.node_tree.links
for node in nodes:
    nodes.remove(node)
bsdf = nodes.new(type='ShaderNodeBsdfPrincipled')
bsdf.inputs["Base Color"].default_value = (0.1, 0.3, 0.8, 1)
bsdf.inputs["Roughness"].default_value = 0.3
output = nodes.new(type='ShaderNodeOutputMaterial')
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])

# 眼睛材质
eye_white_mat = bpy.data.materials.new(name="Eye_White_Material")
eye_white_mat.use_nodes = True
eye_nodes = eye_white_mat.node_tree.nodes
for node in eye_nodes:
    eye_nodes.remove(node)
eye_bsdf = eye_nodes.new(type='ShaderNodeBsdfPrincipled')
eye_bsdf.inputs["Base Color"].default_value = (1, 1, 1, 1)
eye_output = eye_nodes.new(type='ShaderNodeOutputMaterial')
eye_white_mat.node_tree.links.new(eye_bsdf.outputs['BSDF'], eye_output.inputs['Surface'])

# 瞳孔材质
pupil_mat = bpy.data.materials.new(name="Pupil_Material")
pupil_mat.use_nodes = True
pupil_nodes = pupil_mat.node_tree.nodes
for node in pupil_nodes:
    pupil_nodes.remove(node)
pupil_bsdf = pupil_nodes.new(type='ShaderNodeBsdfPrincipled')
pupil_bsdf.inputs["Base Color"].default_value = (0, 0, 0, 1)
pupil_output = pupil_nodes.new(type='ShaderNodeOutputMaterial')
pupil_mat.node_tree.links.new(pupil_bsdf.outputs['BSDF'], pupil_output.inputs['Surface'])

# 分配材质
body.data.materials.append(body_mat)
tail.data.materials.append(body_mat)
dorsal_fin.data.materials.append(body_mat)
left_fin.data.materials.append(body_mat)
right_fin.data.materials.append(body_mat)
ventral_fin.data.materials.append(body_mat)
left_eye_white.data.materials.append(eye_white_mat)
right_eye_white.data.materials.append(eye_white_mat)
left_pupil.data.materials.append(pupil_mat)
right_pupil.data.materials.append(pupil_mat)

# 合并所有部件为一个整体
for obj in [body, tail, dorsal_fin, left_fin, right_fin, ventral_fin,
           left_eye_white, right_eye_white, left_pupil, right_pupil]:
    obj.select_set(True)
bpy.context.view_layer.objects.active = body
bpy.ops.object.join()
fish_obj = bpy.context.active_object
fish_obj.name = "Fish"

# --------------------------
# 创建游动路径
# --------------------------
curve_data = bpy.data.curves.new('Fish_Path', 'CURVE')
curve_data.dimensions = '3D'
curve_data.resolution_u = 32

# 更复杂的游动路径(椭圆形+波浪)
polyline = curve_data.splines.new('POLY')
points = polyline.points
radius_x = 8
radius_y = 6
num_points = 40

points.add(num_points)
for i in range(num_points + 1):
    angle = 2 * math.pi * i / num_points
    z_offset = math.sin(angle * 2) * 1.2  # 上下起伏
    x = radius_x * math.cos(angle)
    y = radius_y * math.sin(angle)
    points[i].co = (x, y, z_offset, 1)

path_obj = bpy.data.objects.new('Fish_Path', curve_data)
bpy.context.collection.objects.link(path_obj)

# --------------------------
# 设置游动动画
# --------------------------
constraint = fish_obj.constraints.new(type='FOLLOW_PATH')
constraint.target = path_obj
constraint.forward_axis = 'FORWARD_X'
constraint.up_axis = 'UP_Z'

frame_start = 1
frame_end = 150
bpy.context.scene.frame_start = frame_start
bpy.context.scene.frame_end = frame_end

# 沿路径移动
constraint.offset = 0
constraint.keyframe_insert(data_path="offset", frame=frame_start)
constraint.offset = 100
constraint.keyframe_insert(data_path="offset", frame=frame_end)

# 更自然的鱼身摆动
def add_swim_animation(frame):
    phase = frame / 15
    # 身体摆动
    fish_obj.rotation_euler.z = math.sin(phase) * 0.3
    # 尾部摆动(幅度更大)
    fish_obj.rotation_euler.y = math.sin(phase * 1.2) * 0.15
    fish_obj.keyframe_insert(data_path="rotation_euler", frame=frame)

for f in range(frame_start, frame_end + 1, 5):
    add_swim_animation(f)

# --------------------------
# 设置摄像机跟踪鱼
# --------------------------
bpy.ops.object.camera_add(location=(0, -10, 5))
camera = bpy.context.active_object
camera.name = "Fish_Camera"
bpy.context.scene.camera = camera

# 添加跟踪约束
track_constraint = camera.constraints.new(type='TRACK_TO')
track_constraint.target = fish_obj
track_constraint.track_axis = 'TRACK_NEGATIVE_Z'  # 摄像机Z轴负方向指向目标
track_constraint.up_axis = 'UP_Y'  # 使用Y轴作为上方向

# 摄像机距离关键帧(使距离有轻微变化)
def add_camera_distance(frame):
    distance = 10 + math.sin(frame / 30) * 1.5  # 距离在9.5-11.5之间变化
    camera.location = (0, -distance, 5 + math.sin(frame / 40) * 0.5)
    camera.keyframe_insert(data_path="location", frame=frame)

for f in range(frame_start, frame_end + 1, 10):
    add_camera_distance(f)

# --------------------------
# 添加环境光照(兼容 Blender 4.3)
# --------------------------
# 主光源(阳光)
bpy.ops.object.light_add(type='SUN', location=(10, 5, 15))
sun = bpy.context.active_object
sun.name = "Sun"
sun.data.energy = 3.0
sun.rotation_euler = (math.radians(60), 0, math.radians(30))

# 辅助光源(从另一侧照亮)
bpy.ops.object.light_add(type='SUN', location=(-10, -5, 10))
fill_light = bpy.context.active_object
fill_light.name = "Fill_Light"
fill_light.data.energy = 1.5  # 较弱的辅助光
fill_light.rotation_euler = (math.radians(45), 0, math.radians(-120))

# 底部补光(减少阴影)
bpy.ops.object.light_add(type='SUN', location=(0, 0, -10))
bottom_light = bpy.context.active_object
bottom_light.name = "Bottom_Light"
bottom_light.data.energy = 0.8  # 非常弱的底部光
bottom_light.rotation_euler = (math.radians(-90), 0, 0)

# 设置环境光遮蔽(Blender 4.3 替代方案)
world = bpy.context.scene.world
if world is None:
    world = bpy.data.worlds.new("World")
    bpy.context.scene.world = world

world.use_nodes = True
nodes = world.node_tree.nodes
links = world.node_tree.links

# 清除所有现有节点
for node in nodes:
    nodes.remove(node)

# 创建背景节点
bg_node = nodes.new('ShaderNodeBackground')
bg_node.inputs["Color"].default_value = (0.05, 0.05, 0.05, 1.0)  # 微弱的环境光颜色
bg_node.inputs["Strength"].default_value = 1.0

# 创建环境光遮蔽节点
ao_node = nodes.new('ShaderNodeAmbientOcclusion')
ao_node.inputs["Distance"].default_value = 2.0

# 创建混合节点
mix_node = nodes.new('ShaderNodeMixShader')
mix_node.inputs["Fac"].default_value = 0.5  # 控制环境光遮蔽的强度

# 创建输出节点
output_node = nodes.new('ShaderNodeOutputWorld')

# 连接节点
links.new(bg_node.outputs["Background"], mix_node.inputs[1])
links.new(ao_node.outputs["Color"], mix_node.inputs[2])
links.new(mix_node.outputs["Shader"], output_node.inputs["Surface"])

# --------------------------
# 添加简单的水下环境
# --------------------------
# 创建一个大球体模拟水体
bpy.ops.mesh.primitive_uv_sphere_add(radius=20, location=(0, 0, 0))
water = bpy.context.active_object
water.name = "Water"
water.scale = (1, 1, 0.5)  # 压扁成椭圆形
water.location.z = -10  # 放到下方

# 水体材质(半透明蓝色)
water_mat = bpy.data.materials.new(name="Water_Material")
water_mat.use_nodes = True
water_nodes = water_mat.node_tree.nodes
for node in water_nodes:
    water_nodes.remove(node)
water_bsdf = water_nodes.new(type='ShaderNodeBsdfGlass')
water_bsdf.inputs["Color"].default_value = (0.2, 0.4, 0.8, 0.2)  # 半透明蓝色
water_output = water_nodes.new(type='ShaderNodeOutputMaterial')
water_mat.node_tree.links.new(water_bsdf.outputs['BSDF'], water_output.inputs['Surface'])
water.data.materials.append(water_mat)

# --------------------------
# 渲染设置
# --------------------------
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.device = 'GPU'  # 使用GPU加速
bpy.context.scene.cycles.samples = 128
bpy.context.scene.render.resolution_x = 1920
bpy.context.scene.render.resolution_y = 1080
bpy.context.scene.render.film_transparent = True  # 透明背景

# --------------------------
# 选择输出目录并渲染
# --------------------------
class RenderPathSelector(bpy.types.Operator, ExportHelper):
    """选择渲染输出目录"""
    bl_idname = "render.select_output_directory"
    bl_label = "选择渲染输出目录"
    
    filename_ext = ""
    directory: bpy.props.StringProperty(subtype='DIR_PATH')
    
    def execute(self, context):
        os.makedirs(self.directory, exist_ok=True)
        context.scene.render.filepath = os.path.join(self.directory, "fish_frame_")
        context.scene.render.image_settings.file_format = 'PNG'
        print(f"渲染保存到: {self.directory}")
        bpy.ops.render.render(animation=True)
        return {'FINISHED'}

# 注册并调用文件选择器
bpy.utils.register_class(RenderPathSelector)
bpy.ops.render.select_output_directory('INVOKE_DEFAULT')s

  • 本文来自 oeasy Python 系统教程。
  • 想完整、扎实学 Python,
  • 搜索 oeasy 即可。