Загрузка данных


# ASUS ROG Astral GeForce RTX 5090 — procedural Blender model
# Run in Blender: Scripting > New > paste this file > Run Script.
# Detailed procedural model based on ASUS/ROG published specs and reference photos.
# Includes procedural micro-bump/roughness, layered armour, fasteners, dense fins and PCB components.
# Units are millimetres (1 Blender unit = 1 mm). Not a manufacturing/CAD model.

import bpy
import math
from mathutils import Vector

# ---------- Scene ----------
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
for datablocks in (bpy.data.materials,):
    for block in list(datablocks):
        if block.users == 0:
            datablocks.remove(block)

scene = bpy.context.scene
scene.unit_settings.system = 'NONE'

# ---------- Materials ----------
def mat(name, color, metallic=0.0, roughness=0.4, emission=None):
    m = bpy.data.materials.new(name)
    m.diffuse_color = (*color, 1)
    m.use_nodes = True
    nt = m.node_tree
    nt.nodes.clear()
    out = nt.nodes.new('ShaderNodeOutputMaterial')
    bsdf = nt.nodes.new('ShaderNodeBsdfPrincipled')
    bsdf.inputs['Base Color'].default_value = (*color, 1)
    bsdf.inputs['Metallic'].default_value = metallic
    bsdf.inputs['Roughness'].default_value = roughness
    if emission:
        if 'Emission Color' in bsdf.inputs:
            bsdf.inputs['Emission Color'].default_value = (*emission, 1)
            bsdf.inputs['Emission Strength'].default_value = 0.7
        elif 'Emission' in bsdf.inputs:
            bsdf.inputs['Emission'].default_value = (*emission, 1)
    # Procedural surface finish: fine machining grain + subtle bump.
    # Every material gets a stable node tree; no node-name lookups are used.
    texcoord = nt.nodes.new('ShaderNodeTexCoord')
    noise = nt.nodes.new('ShaderNodeTexNoise')
    noise.inputs['Scale'].default_value = 185.0 if metallic > 0.3 else 95.0
    noise.inputs['Detail'].default_value = 2.0
    noise.inputs['Roughness'].default_value = 0.72
    bump = nt.nodes.new('ShaderNodeBump')
    bump.inputs['Strength'].default_value = 0.12 if metallic > 0.3 else 0.075
    bump.inputs['Distance'].default_value = 0.08
    ramp = nt.nodes.new('ShaderNodeValToRGB')
    ramp.color_ramp.elements[0].position = 0.18
    ramp.color_ramp.elements[1].position = 0.82
    # Keep the bump subtle; this is microfinish, not a lumpy surface.
    nt.links.new(texcoord.outputs['Generated'], noise.inputs['Vector'])
    nt.links.new(noise.outputs['Fac'], bump.inputs['Height'])
    nt.links.new(bump.outputs['Normal'], bsdf.inputs['Normal'])
    # Slight roughness variation makes black anodized metal and plastic read better.
    nt.links.new(noise.outputs['Fac'], ramp.inputs['Fac'])
    nt.links.new(ramp.outputs['Color'], bsdf.inputs['Roughness'])
    nt.links.new(bsdf.outputs['BSDF'], out.inputs['Surface'])
    return m

black = mat('Graphite | die-cast shroud', (0.035, 0.038, 0.043), 0.45, 0.29)
black2 = mat('Backplate black', (0.018, 0.020, 0.024), 0.35, 0.32)
plastic = mat('Fan blades', (0.012, 0.014, 0.019), 0.15, 0.3)
metal = mat('Brushed aluminium', (0.42, 0.44, 0.47), 0.8, 0.25)
silver = mat('Fan ring silver', (0.62, 0.64, 0.67), 0.82, 0.22)
gold = mat('PCIe contacts', (0.72, 0.48, 0.12), 0.75, 0.24)
pcbmat = mat('PCB', (0.025, 0.055, 0.045), 0.15, 0.45)
portmat = mat('Port interiors', (0.005, 0.006, 0.008), 0.25, 0.28)
rgb = mat('Subtle ROG RGB', (0.10, 0.015, 0.42), 0.25, 0.24, (0.12, 0.01, 0.65))
white = mat('Lettering', (0.75, 0.77, 0.8), 0.2, 0.35)
rubber = mat('Fan hub rubberized polymer', (0.025, 0.027, 0.032), 0.05, 0.62)
copper = mat('Heatpipe copper', (0.48, 0.19, 0.075), 0.82, 0.25)
solder = mat('PCB solder and component metal', (0.34, 0.36, 0.39), 0.82, 0.26)

# ---------- Helpers ----------
def cube(name, loc, scale, material, bevel=0):
    bpy.ops.mesh.primitive_cube_add(size=1, location=loc)
    o = bpy.context.object
    o.name = name
    o.dimensions = scale
    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
    if material: o.data.materials.append(material)
    if bevel:
        mod = o.modifiers.new('Machined softened edges', 'BEVEL')
        mod.width = bevel
        mod.segments = 3
        o.modifiers.new('Weighted corner normals', 'WEIGHTED_NORMAL')
    return o

def cyl(name, loc, radius, depth, material, axis='Z', vertices=64, bevel=0):
    bpy.ops.mesh.primitive_cylinder_add(vertices=vertices, radius=radius, depth=depth, location=loc)
    o = bpy.context.object
    o.name = name
    if axis == 'X': o.rotation_euler[1] = math.pi/2
    elif axis == 'Y': o.rotation_euler[0] = math.pi/2
    if material: o.data.materials.append(material)
    if bevel:
        mod=o.modifiers.new('Edge glint', 'BEVEL'); mod.width=bevel; mod.segments=2
        o.modifiers.new('Weighted normals','WEIGHTED_NORMAL')
    return o

def text_obj(body, name, loc, size, material, rotation=(0,0,0), extrude=0.15):
    curve = bpy.data.curves.new(name, 'FONT')
    curve.body = body
    curve.size = size
    curve.extrude = extrude
    curve.bevel_depth = 0.025
    curve.bevel_resolution = 1
    ob = bpy.data.objects.new(name, curve)
    bpy.context.collection.objects.link(ob)
    ob.location = loc
    ob.rotation_euler = rotation
    ob.data.materials.append(material)
    return ob

def blade_mesh(name, cx, cy, z, angle, radius=31, material=plastic, reverse=False):
    # Broad swept, tapered fan blade silhouette, built as a shallow extruded mesh.
    # The curved sweep is approximated with a four-point profile.
    pts = [(7.0, -3.5), (radius*0.53, -10.0), (radius*0.94, -4.0), (radius*0.72, 7.0), (radius*0.38, 10.0), (13.0, 5.0)]
    if reverse:
        pts = [(x, -y) for x,y in pts]
    ca, sa = math.cos(angle), math.sin(angle)
    verts=[]
    for zz in (-0.9,0.9):
        for x,y in pts:
            verts.append((cx+x*ca-y*sa, cy+x*sa+y*ca, z+zz))
    n=len(pts)
    faces=[tuple(range(n)), tuple(range(n,2*n))]
    for i in range(n):
        j=(i+1)%n
        faces.append((i,j,j+n,i+n))
    mesh=bpy.data.meshes.new(name+' mesh'); mesh.from_pydata(verts,[],faces); mesh.materials.append(material); mesh.update()
    ob=bpy.data.objects.new(name,mesh); bpy.context.collection.objects.link(ob)
    bevel=ob.modifiers.new('Soft blade edges','BEVEL'); bevel.width=0.6; bevel.segments=2
    ob.modifiers.new('Blade normals','WEIGHTED_NORMAL')
    return ob

def make_fan(cx, cy, face_z, label, reverse=False):
    sign = 1 if face_z > 0 else -1
    # shroud surround and silver trim ring
    cyl(label+' outer fan ring', (cx,cy,face_z), 43.5, 2.2, black, 'Z', 96, 0.5)
    cyl(label+' silver accent ring', (cx,cy,face_z+sign*1.35), 41.0, 1.0, silver, 'Z', 96, 0.25)
    cyl(label+' inner ring', (cx,cy,face_z+sign*1.95), 37.8, 1.2, black, 'Z', 96, 0.25)
    blade_z = face_z + sign*2.1
    for i in range(7):
        blade_mesh(label+' axial-tech blade %02d'%i, cx,cy,blade_z,2*math.pi*i/7+0.18, 31.5, plastic, reverse)
    cyl(label+' central hub', (cx,cy,face_z+sign*4.0), 14.0, 4.0, black, 'Z', 64, 1.0)
    cyl(label+' hub badge', (cx,cy,face_z+sign*6.15), 10.8, 0.8, silver, 'Z', 64, 0.3)
    cyl(label+' hub centre', (cx,cy,face_z+sign*6.65), 9.5, 0.8, black2, 'Z', 64, 0.25)
    torus(label+' hub fine rim', (cx,cy,face_z+sign*7.12), 8.5, 0.32, silver)
    for k in range(7):
        a=2*math.pi*k/7
        # Small blade-root fastener details on the fan frame
        screw(label+' frame micro screw %02d'%k, cx+35.5*math.cos(a), cy+35.5*math.sin(a),
              face_z+sign*2.55, radius=0.72, material=metal)
    # A small ROG-style badge mark; avoids relying on external logo files.
    text_obj('ROG', label+' hub lettering', (cx-6.6,cy-2.8,face_z+sign*7.12), 4.6, white,
             rotation=(0,0,0), extrude=0.05)


# ---------- Fine detail helpers ----------
def torus(name, loc, major, minor, material, rotation=(0,0,0), major_segments=64, minor_segments=10):
    bpy.ops.mesh.primitive_torus_add(major_segments=major_segments, minor_segments=minor_segments,
        location=loc, major_radius=major, minor_radius=minor)
    o=bpy.context.object; o.name=name; o.rotation_euler=rotation
    if material: o.data.materials.append(material)
    for poly in o.data.polygons: poly.use_smooth=True
    return o

def screw(name, x, y, z, radius=1.6, material=silver, axis='Z'):
    cyl(name+' head', (x,y,z), radius, 0.8, material, axis, 24, 0.18)
    # Phillips slot represented by two fine dark cuts.
    if axis == 'Z':
        cube(name+' slot A', (x,y,z+0.43), (radius*1.25,0.28,0.12), black2, 0.04)
        cube(name+' slot B', (x,y,z+0.44), (0.28,radius*1.25,0.12), black2, 0.04)
    else:
        cube(name+' slot', (x+0.43,y,z), (0.12,radius*1.25,0.28), black2, 0.04)

def vent_slot(name, loc, size, material=portmat, bevel=0.7):
    return cube(name, loc, size, material, bevel)

# ---------- Card body: ASUS published overall dimensions 357.6 x 149.3 x 76 mm ----------
L, W, T = 357.6, 149.3, 76.0
# Main die-cast cooler and central PCB
cube('Main cooler chassis', (0,0,0), (L-7,W-7,T-8), black, 7.0)
cube('PCB core', (0,0,-8), (L-14,W-14,5), pcbmat, 1.2)
# Large front face plate under the three visible fans
cube('Front fan shroud', (0,0,T/2-1.2), (L-9,W-8,5.5), black, 6.0)
# Backplate face
cube('Full metal backplate', (0,0,-T/2+1.2), (L-8,W-8,4.5), black2, 5.0)
cube('Backplate inset panel', (12,0,-T/2-1.3), (L-50,W-28,1.1), black, 3.0)

# Three front fans, matching the provided product views
fan_x = [-111.0, 0.0, 111.0]
for i,x in enumerate(fan_x,1):
    make_fan(x, 0, T/2+2.2, 'Front fan %d'%i, False)

# Rear fourth fan on the backplate (Astral's four-fan cooling design)
make_fan(-126.0, 0, -T/2-2.2, 'Rear backplate fan', True)

# Radiator fins visible around the long side edges
for i in range(70):
    x = -166 + i*4.8
    cube('Cooling fin %02d'%i, (x, W/2-2.5, 0), (1.15, 4.5, 48), metal, 0.25)
    cube('Cooling fin underside %02d'%i, (x, -W/2+2.5, 0), (1.15, 4.5, 48), metal, 0.25)

# Side rail and brand stripe on one long side
cube('Side rail branding strip', (0,W/2+0.2,8), (L-18,2.0,12), black2, 1.5)
text_obj('GEFORCE RTX', 'GeForce RTX side lettering', (42,W/2+1.45,4.2), 8.0, white,
         rotation=(math.pi/2,0,0), extrude=0.04)
text_obj('REPUBLIC OF GAMERS', 'ROG side lettering', (-142,W/2+1.45,4.2), 4.2, silver,
         rotation=(math.pi/2,0,0), extrude=0.04)

# Backplate detailing: geometric panel seams, screws, and restrained logo text
for x in [-145,-90,-35,25,85,140]:
    cube('Backplate etched seam', (x,0,-T/2-2.05), (0.8,82,0.35), black, 0.12)
text_obj('REPUBLIC OF GAMERS', 'Backplate ROG lettering', (42,47,-T/2-2.5), 7.0, white,
         rotation=(0,0,0), extrude=0.04)
# Four mounting screws on backplate
for x in [-155,155]:
    for y in [-57,57]:
        cyl('Backplate screw', (x,y,-T/2-2.2), 2.7, 0.8, silver, 'Z', 24, 0.15)
        cube('Screw slot', (x,y,-T/2-2.72), (2.8,0.45,0.18), black2, 0.05)

# PCIe edge connector along lower edge, with individual gold fingers
cube('PCIe connector substrate', (-10,-W/2+1.0,-T/2-5.2), (116,10,3.5), black2, 0.5)
for i in range(48):
    x=-65+i*2.25
    cube('PCIe gold contact %02d'%i, (x,-W/2+0.1,-T/2-7.05), (1.35,7.0,0.55), gold, 0.12)

# Rear I/O bracket at the left end, with 3 DisplayPort + 2 HDMI apertures.
# Port face points toward -X; all five ports are shown along the bracket height.
cube('I O bracket', (-L/2-2.3,0,0), (4.5,W-20,T-5), metal, 1.2)
# Ventilation slots across bracket
for y in [-48,-36,-24,-12,0,12,24,36,48]:
    cube('I O bracket vent', (-L/2-4.7,y,12), (0.7,5.5,25), portmat, 0.7)
# Five port cutout-like dark face plates in a row
port_y = [-43,-22,0,22,43]
for i,y in enumerate(port_y):
    is_hdmi = i in (1,4)
    w = 13.5 if is_hdmi else 15.0
    h = 5.5 if is_hdmi else 4.0
    cube(('HDMI' if is_hdmi else 'DisplayPort')+' socket rim', (-L/2-5.0,y,-18), (1.2,w+2.5,h+2.5), black2, 0.8)
    cube(('HDMI' if is_hdmi else 'DisplayPort')+' socket opening', (-L/2-5.75,y,-18), (0.6,w,h), portmat, 0.6)
# Small port labels
text_obj('HDMI   DP   DP   DP   HDMI', 'I O labels', (-L/2-6.0,-50,-31), 3.0, white,
         rotation=(math.pi/2,0,-math.pi/2), extrude=0.025)

# 16-pin power connector on upper edge, offset toward one end
cube('16 pin power socket surround', (115,W/2-7,8), (25,11,15), black2, 2.0)
cube('16 pin power socket cavity', (115,W/2-1.2,8), (18,1.0,9), portmat, 0.6)
for row in range(2):
    for col in range(6):
        cube('Power pin %d %d'%(row,col), (108+col*2.8,W/2-0.55,5.7+row*3.2), (1.2,0.35,1.2), gold, 0.15)

# Subtle RGB accents along the upper shroud edge
for x in [-105,0,105]:
    cube('RGB accent diffuser', (x,W/2-10,T/2+2.0), (43,2.2,1.1), rgb, 0.45)


# ---------- Added high-detail product geometry ----------
# Beveled geometric armour panels on the fan shroud
for idx, x in enumerate([-160,-54,54,160]):
    cube('Armour panel inset %d'%idx, (x,-W/2+13,T/2+1.7), (42,15,1.25), black2, 2.0)
    cube('Armour panel highlight %d'%idx, (x,-W/2+13,T/2+2.38), (30,0.65,0.25), metal, 0.12)

# More convincing radiator fin stack visible through the open side
for i in range(100):
    x=-169+i*3.4
    cube('Dense radiator fin %03d'%i, (x,W/2-7,0), (0.72,9.0,50), metal, 0.12)
    cube('Dense radiator fin lower %03d'%i, (x,-W/2+7,0), (0.72,9.0,50), metal, 0.12)

# Long side ventilation openings and structural ribs
for i in range(23):
    x=-148+i*13.2
    vent_slot('Side exhaust vent %02d'%i, (x,W/2+1.35,-17), (7.5,0.65,12), portmat, 1.1)
    cube('Side frame rib %02d'%i, (x+5.3,W/2+1.4,-17), (1.1,0.8,14), black, 0.3)

# Heat pipes / vapor-chamber edge hints running along the cooler core
for yy in [-31,-21,-11,0,11,21,31]:
    cyl('Heatpipe edge detail', (0,yy,-1), 1.35, L-42, copper, 'X', 20, 0.15)

# Backplate design: additional recessed geometric accents and corner protection
for idx, x in enumerate([-125,-65,0,65,125]):
    cube('Backplate angular groove %d'%idx, (x,-42,-T/2-2.25), (42,1.0,0.32), black2, 0.25)
    cube('Backplate angular groove return %d'%idx, (x+18,-30,-T/2-2.25), (1.0,24,0.32), black2, 0.25)
for x in [-165,165]:
    cube('Backplate corner guard', (x,0,-T/2-2.1), (11,92,1.2), black, 2.0)
    for y in [-38,38]:
        screw('Backplate corner screw', x,y,-T/2-3.0, radius=2.1)

# A more detailed PCIe bracket flange and screws
for yy in [-61,-48,48,61]:
    screw('I O bracket screw', -L/2-5.2, yy, 25, radius=2.0, axis='X')
    screw('I O bracket lower screw', -L/2-5.2, yy, -25, radius=1.7, axis='X')

# Gold connector fingers and PCB surface components
for i in range(22):
    x=-155+i*14.5
    cube('PCB power stage component %02d'%i, (x,-W/2+13,-11), (8.0,7.0,3.2), black2, 0.45)
    cube('Power stage metal cap %02d'%i, (x,-W/2+13,-9.1), (5.4,4.2,0.45), solder, 0.25)
for i in range(16):
    x=-130+i*17
    cube('PCB capacitor %02d'%i, (x,-W/2+23,-12), (4.2,4.2,4.8), metal, 0.65)
    cyl('Capacitor top %02d'%i, (x,-W/2+23,-9.4), 1.45, 0.35, silver, 'Z', 20, 0.08)

# Power connector detail: outer lip, latch, and individual contacts
cube('16-pin socket metallic lip', (115,W/2-0.6,8), (22,1.1,12), metal, 1.0)
cube('16-pin latch', (115,W/2-0.2,15.5), (7,1.3,2.2), black, 0.45)
for row in range(2):
    for col in range(6):
        cube('16-pin contact glint %d %d'%(row,col),
             (108+col*2.8,W/2-0.31,5.7+row*3.2), (0.75,0.18,0.65), gold, 0.08)

# Small identification plate and etched technical markings on the backplate
cube('Serial plate', (118,18,-T/2-2.6), (35,17,0.7), metal, 0.7)
cube('Serial plate black inset', (118,18,-T/2-3.05), (32,14,0.35), black2, 0.4)
text_obj('ROG ASTRAL', 'Serial plate title', (105,14,-T/2-3.3), 3.2, white, extrude=0.025)
text_obj('RTX 5090', 'Serial plate model', (108,20,-T/2-3.3), 2.6, silver, extrude=0.025)

# Fan frame accent ticks around the outer silver rings
for cx in fan_x:
    for i in range(24):
        a=2*math.pi*i/24
        x=cx+40.0*math.cos(a); y=40.0*math.sin(a)
        tick=cube('Fan ring index mark', (x,y,T/2+4.1), (1.2,0.55,0.35), black2, 0.08)
        tick.rotation_euler[2]=a

# Tiny rubber isolation pads and screw heads around shroud edges
for x in [-165,-112,-56,0,56,112,165]:
    for y in [-62,62]:
        cyl('Shroud isolation pad', (x,y,T/2+1.9), 1.6, 0.8, rubber, 'Z', 24, 0.15)
        screw('Shroud fastener', x,y,T/2+2.5, radius=1.0, material=metal)

# Side branding line and small warning label
cube('Side accent line', (0,W/2+1.3,18), (L-32,0.9,0.9), silver, 0.25)
cube('Side warning label', (-115,W/2+1.55,-24), (36,0.6,7), black2, 0.3)
text_obj('CAUTION / HIGH POWER', 'Side warning text', (-131,W/2+2.0,-26), 2.1, white,
         rotation=(math.pi/2,0,0), extrude=0.02)

# ---------- Presentation setup ----------
# Ground plane and camera are included for an immediately viewable render.
groundmat = mat('Studio floor', (0.025,0.028,0.034), 0.05, 0.5)
cube('Studio floor', (0,0,-105), (900,650,3), groundmat, 0)

def point_at(obj, target):
    obj.rotation_euler = (Vector(target)-obj.location).to_track_quat('-Z','Y').to_euler()

bpy.ops.object.camera_add(location=(475,-470,390))
camera=bpy.context.object
camera.name='Product hero camera'
point_at(camera,(0,0,0))
camera.data.type='ORTHO'
camera.data.ortho_scale=570
scene.camera=camera

def area_light(name, loc, energy, size, target=(0,0,0)):
    bpy.ops.object.light_add(type='AREA', location=loc)
    l=bpy.context.object; l.name=name; l.data.energy=energy; l.data.shape='DISK'; l.data.size=size
    point_at(l,target)
area_light('Key softbox',(100,-260,360),2400,260)
area_light('Fill softbox',(-260,-80,180),1600,220)
area_light('Rim softbox',(40,260,260),2600,240)

scene.render.engine='CYCLES'
scene.cycles.samples=64
scene.render.resolution_x=1800
scene.render.resolution_y=1250
scene.render.resolution_percentage=100
scene.render.image_settings.file_format='PNG'
scene.render.film_transparent=False
scene.world.color=(0.025,0.025,0.025)

# Set useful viewport angle and select the whole card chassis.
bpy.ops.object.select_all(action='DESELECT')
body=bpy.data.objects.get('Main cooler chassis')
if body:
    body.select_set(True)
    bpy.context.view_layer.objects.active=body
for area in bpy.context.screen.areas:
    if area.type=='VIEW_3D':
        area.spaces.active.region_3d.view_distance=520
        area.spaces.active.region_3d.view_location=(0,0,0)

print('ROG Astral RTX 5090 model created. Dimensions approx. 357.6 x 149.3 x 76 mm.')