3D模型AI生成技术分享

3D模型生成服务-tripo ai

一、获取api-key

点击tripo ai aky获取api-key,查看使用文档,有免费生成的积分,每个月送300积分

二、经验分享

经过我的尝试发现,它的生成3D模型接口,图片来源有3种方式:第一种先使用upload(api.tripo3d.ai/v2/openapi/...)接口上传得到一个file_token、第二种使用upload STS(api.tripo3d.ai/v2/openapi/...%25E6%258E%25A5%25E5%258F%25A3%25E5%25BE%2597%25E5%2588%25B0%25E5%25AF%25B9%25E8%25B1%25A1%25E5%25AD%2598%25E5%2582%25A8%25E7%259A%2584%25E4%25BF%25A1%25E6%2581%25AF%25E5%2590%258E%25E7%25BB%25AD%25E4%25BD%25BF%25E7%2594%25A8%25E3%2580%2581%25E7%25AC%25AC%25E4%25B8%2589%25E7%25A7%258D%25E7%259B%25B4%25E6%258E%25A5%25E6%258F%2590%25E4%25BE%259B%25E5%259B%25BE%25E7%2589%2587%25E7%259A%2584url%25EF%25BC%258C%25E6%2588%2591%25E6%259C%2580%25E6%258E%25A8%25E8%258D%2590%25E7%259A%2584%25E6%2598%25AF%25E7%25AC%25AC%25E4%25B8%2589%25E7%25A7%258D%25EF%25BC%258C%25E5%259B%25A0%25E4%25B8%25BA%25E5%258F%25AF%25E4%25BB%25A5%25E5%25B0%2591%25E5%258F%2591%25E9%2580%2581%25E4%25B8%2580%25E4%25B8%25AA%25E8%25AF%25B7%25E6%25B1%2582%25E3%2580%2582 "https://api.tripo3d.ai/v2/openapi/upload/sts/token)%E6%8E%A5%E5%8F%A3%E5%BE%97%E5%88%B0%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8%E7%9A%84%E4%BF%A1%E6%81%AF%E5%90%8E%E7%BB%AD%E4%BD%BF%E7%94%A8%E3%80%81%E7%AC%AC%E4%B8%89%E7%A7%8D%E7%9B%B4%E6%8E%A5%E6%8F%90%E4%BE%9B%E5%9B%BE%E7%89%87%E7%9A%84url%EF%BC%8C%E6%88%91%E6%9C%80%E6%8E%A8%E8%8D%90%E7%9A%84%E6%98%AF%E7%AC%AC%E4%B8%89%E7%A7%8D%EF%BC%8C%E5%9B%A0%E4%B8%BA%E5%8F%AF%E4%BB%A5%E5%B0%91%E5%8F%91%E9%80%81%E4%B8%80%E4%B8%AA%E8%AF%B7%E6%B1%82%E3%80%82")

三、最终确定方案

如果没有对象存储服务的话,就要老老实实走三个步骤:上传图片、创建3D模型生成任务和查询任务进度

四、接口解析讲解

上传图片(可选):

ini 复制代码
import requests

# 把刚刚申请好的api-key替换填充
api_key = "tsk_***"
url = "https://api.tripo3d.ai/v2/openapi/upload/sts"

headers = {"Authorization": f"Bearer {api_key}"}

file_path = 'cat.jpeg'with open(file_path, 'rb') as f:
    files = {'file': (file_path, f, 'image/jpeg')}
    response = requests.post(url, headers=headers, files=files)print(response.json())
json 复制代码
{"code": 0,"data": {"image_token": "xxxxxxxxxxx"}}
// 这个image_token,后面填入模型生成接口的file_token中
  • 特别说明:文件格式仅支持webp/jpeg,图片像素支持在20px到6000px之间的,推荐超过256px最好

生成模型接口,有4种,分别是图生模型、文字生模型、多文件生成模型和改进模型,此处只讲解图生模型

图生3D模型请求参数解析

json 复制代码
{
    "type": "image_to_model", // 必填字段,对应生成模型的任务类型
    "model_version": "v3.0-20250812", // 非必填参数,默认为v2.5-20250123
    "file": {
        "type": "jpg" // 建议填充
        "file-token": "xxxxxxxxxxxxxxxxxxxx" // 把刚刚得到的image_token填进去
        // 补充说明一点,file-token、url、object这个三个方式是互斥的,只会有一个生效
        // 也就是说,我现在选了file-token方式后面的file参数填什么都没用了
        "url": "https://img-baofun.zhhainiao.com/fs/a9c93a228f6ffb86e1f70beec553422b.jpg"
         //直接可以访问到的图片地址
         "object": {
             "bucket": "tripo-data" //通常为tripo-data,当然你也可以自己定义,确保你的对象存储桶中一定要有这个目录
             "key": "xxxxxxxxxxxxxx" //把在之前在upload STS接口中拿到的session_token放进去
         }
    },
    "model_seed": 84848, // 是个整数就行,不写它就随机选择,该种子控制几何生成过程,确保在使用相同种子时生成相同的模型,是个随机数保持一致即可
    "enable_image_autofix": false // 默认为false,true的话效果更好,但代价是耗时更长
    // 以下参数只针对,模型版本大于v2.0-20240919以上的,低于这个版本的填了也没用
    "face_limit": 1000, // 是个整数,默认不填的话会自适应面数限制
    "texture": true, // 默认为true,表明带有纹理
    "pbr": true, // 默认为true,表示为物理渲染,一旦设置为这个前面的纹理设置将会失效
    "texture_seed": 838383,//也是个整数,随机数不设置的话就默认生成,如果你想要不同纹理模型就自己调整纹理种子,但是模型种子要保持不变
    "texture_alignment": "original_image", //纹理对齐,默认为original_image,可以填入两个参数,选择original_image的话就会优先保证与源图像视觉效果
    // geometry,在几何模式下,会先考虑3D结构准确性
    "texture_quality": "standard", //默认为标准(standard),还有另一个参数为detailed,会提高纹理的清晰度,更加逼真地显示复杂部件
    "auto_size": false, // 该设置会自动把模型缩放为现实真实世界比例尺寸,单位为米。默认值为false
    "orientation": "default", //默认就是default,也可以设置为与原始图像对齐(align_image)
    "quad": false, // 默认为false,一旦启用此项,前面的面数设置将会失效,并且强制输出为FBX模型
    "compress": "meshopt", // 应用于纹理的压缩类型,除了默认meshopt 网状压缩之外,还有几何压缩(geometry)
    "smart_low_poly": false, // 默认为false,打印3D模型复杂度低的就开启这个效果最好,太过于复杂的就不要开启了  
    "generate_parts": false, // 默认为false,和texture、pbr等互斥,如果仅仅只是生成部件的话,需要把texture、pbr都设置为true
    // 以下参数为模型版本大于v3.0-20250812,才可以使用
    "geometry_quality": "standard" // 设置为detailed,将提供最复杂、最逼真的模型,标准模式下是对细节和速度的平衡
    // 换句话说,想要细节,就选detailed,想要速度,就选standard
}
python 复制代码
import requests
import json

api_key = "tsk_***"
url = "https://api.tripo3d.ai/v2/openapi/task"

data = {"type": "image_to_model","file": {"type": "jpg","file_token": "***"}}

headers = {"Content-Type": "application/json","Authorization": f"Bearer {api_key}"}

response = requests.post(url, headers=headers, json=data)print(response.json())
json 复制代码
{"code": 0,"data": {"task_id": "1ec04ced-4b87-44f6-a296-beee80777941"}}

查询任务接口,不管是调用了什么类型生成接口,都要在这里查询任务进度

响应参数解析

json 复制代码
{
    "task_id": "xxxxxxxxxxxxxxx", //任务唯一标识
    "type": "image_to_model", // 与你创建了什么模型任务,这里以图生模型为例
    "status": "success", // 状态分为两种,一种是中间态(queued、running),另一种是最终态(success、failed、banned、expired、cancelled、unknown)
    // 特别说明:失败、过期和未知这三种状态,需要拿任务id去咨询技术服务团队
    // 如果是被禁用了,就想想自己玩了什么骚操作,建议也向技术团队咨询一下
    // 如果是取消了,我也没遇过
    "input": {}, // 输入对象因使用者而有所不同
    "ouput": { // 注意3D模型或者图片的下载时间喔,只有5分钟
        "model": "xxxxxxxxx", // 模型下载地址
        "base_model": "xxxxx", // 基础模型下载地址
        "pbr_model": "xxxxxxxx", //pbr模型下载地址
        "generated_image": "xxxxxxxxx", //生成图像的URL
        "rendered_image": "xxx", // 模型预览的URL
    },
    "progress": 0-100, // 进度条,用于用户页显示
    "create_time": 148894989438, // 创建的时间戳
}
python 复制代码
import requests

api_key = "tsk_***"
task_id = "xxxxxxxxx"
url = f"https://api.tripo3d.ai/v2/openapi/task/{task_id}"

headers = {"Authorization": f"Bearer {api_key}"}

response = requests.get(url, headers=headers)print(response.json())
json 复制代码
{
"code": 0,
"data": {
    "task_id": "xxxxxxxxxxxxxxx",
    "type": "text_to_model",
    "status": "running",
    "input": {"prompt": "a small cat"},
    "output": {},
    "progress": 99,
    "create_time": 1709048933
 }
}

最终demo

python 复制代码
import requests
import time
import random
from typing import Optional, Dict, Any

# ===================== 配置项 =====================
API_KEY = "tsk_xxxxxxxxxxxxxxxxxx"  # 替换为你的真实API Key
IMAGE_FILE_PATH = "doro.webp"  # 图片路径
TASK_POLL_INTERVAL = 5  # 轮询间隔(秒)
TIMEOUT_SECONDS = 300  # 任务超时时间(5分钟)

# 模型生成参数配置(根据需求调整)
MODEL_CONFIG = {
    "type": "image_to_model",
    "model_version": "v3.0-20250812",  # 使用v3版本
    "file": {
        "type": "webp",  # 图片类型
        "file_token": ""  # 上传图片后填充
    },
    "model_seed": 1000,  # 随机模型种子
    "enable_image_autofix": True,  # 开启图片自动修复(效果更好)
    # v2.0+ 版本参数
    # "face_limit": 2000,  # 面数限制
    "texture": True,  # 启用纹理(pbr为false时生效)
    "pbr": False,  # 关闭PBR,使用普通纹理
    "texture_seed": 1000,  # 纹理种子
    "texture_alignment": "original_image",  # 优先保证视觉效果
    "texture_quality": "detailed",  # 高清纹理
    "auto_size": False,  # 不自动缩放尺寸
    "orientation": "default",  # 默认朝向
    "quad": False,  # 不强制FBX格式
    # "compress": "meshopt",  # 网状压缩
    "smart_low_poly": False,  # 不启用低多边形优化
    "generate_parts": False,  # 不生成部件
    # v3.0+ 版本参数
    "geometry_quality": "detailed"  # 高精度几何模型
}


# ===================== 核心函数 =====================
class TripoAIClient:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        self.session = requests.Session()  # 复用会话,提升性能

    def upload_image(self, file_path: str) -> Optional[str]:
        """
        上传图片并获取image_token
        :param file_path: 本地图片路径
        :return: image_token或None
        """
        url = "https://api.tripo3d.ai/v2/openapi/upload/sts"

        # 检测文件是否存在
        if not os.path.exists(file_path):
            print(f"错误:图片文件 {file_path} 不存在")
            return None

        # 获取文件类型
        file_ext = os.path.splitext(file_path)[1].lstrip('.').lower()
        content_type = f"image/{file_ext}"

        try:
            print(f"正在上传图片:{file_path}")
            with open(file_path, 'rb') as f:
                files = {'file': (os.path.basename(file_path), f, content_type)}
                # 上传不需要Content-Type: application/json
                upload_headers = {"Authorization": f"Bearer {self.api_key}"}
                response = self.session.post(
                    url,
                    headers=upload_headers,
                    files=files,
                    timeout=30
                )

            response.raise_for_status()  # 抛出HTTP错误
            result = response.json()

            if result.get("code") == 0 and "image_token" in result.get("data", {}):
                image_token = result["data"]["image_token"]
                print(f"图片上传成功,image_token:{image_token}")
                return image_token
            else:
                print(f"图片上传失败:{result}")
                return None

        except requests.exceptions.RequestException as e:
            print(f"图片上传请求异常:{str(e)}")
            return None

    def create_model_task(self, image_token: str) -> Optional[str]:
        """
        创建3D模型生成任务
        :param image_token: 上传图片返回的token
        :return: task_id或None
        """
        url = "https://api.tripo3d.ai/v2/openapi/task"

        # 填充file_token
        MODEL_CONFIG["file"]["file_token"] = image_token

        try:
            print(f"正在创建3D模型生成任务,配置:{MODEL_CONFIG}")
            response = self.session.post(
                url,
                headers=self.headers,
                json=MODEL_CONFIG,
                timeout=30
            )

            response.raise_for_status()
            result = response.json()

            if result.get("code") == 0 and "task_id" in result.get("data", {}):
                task_id = result["data"]["task_id"]
                print(f"任务创建成功,task_id:{task_id}")
                return task_id
            else:
                print(f"任务创建失败:{result}")
                return None

        except requests.exceptions.RequestException as e:
            print(f"创建任务请求异常:{str(e)}")
            return None

    def query_task_status(self, task_id: str) -> Dict[str, Any]:
        """
        查询任务状态
        :param task_id: 任务ID
        :return: 任务状态信息
        """
        url = f"https://api.tripo3d.ai/v2/openapi/task/{task_id}"

        try:
            response = self.session.get(
                url,
                headers=self.headers,
                timeout=30
            )

            response.raise_for_status()
            return response.json()

        except requests.exceptions.RequestException as e:
            print(f"查询任务状态异常:{str(e)}")
            return {"code": -1, "error": str(e)}

    def poll_task_until_complete(self, task_id: str) -> Optional[Dict[str, Any]]:
        """
        轮询任务直到完成/失败/超时
        :param task_id: 任务ID
        :return: 最终任务结果或None
        """
        start_time = time.time()
        final_status = ["success", "failed", "banned", "expired", "cancelled", "unknown"]

        print(f"\n开始轮询任务状态(间隔{TASK_POLL_INTERVAL}秒),task_id:{task_id}")
        print("-" * 50)

        while True:
            # 检查超时
            elapsed = time.time() - start_time
            if elapsed > TIMEOUT_SECONDS:
                print(f"任务超时({TIMEOUT_SECONDS}秒),终止轮询")
                return None

            # 查询状态
            result = self.query_task_status(task_id)

            if result.get("code") != 0:
                print(f"查询失败:{result.get('error', '未知错误')}")
                time.sleep(TASK_POLL_INTERVAL)
                continue

            data = result.get("data", {})
            status = data.get("status", "unknown")
            progress = data.get("progress", 0)

            print(f"当前状态:{status} | 进度:{progress}% | 耗时:{int(elapsed)}秒")

            # 检查是否为最终状态
            if status in final_status:
                print("-" * 50)
                if status == "success":
                    print("任务执行成功!")
                    print(f"pbr模型下载地址:{data.get('output', {}).get('pbr_model', '无')}")
                    print(f"基础模型下载地址:{data.get('output', {}).get('base_model', '无')}")
                    print(f"模型下载地址:{data.get('output', {}).get('model', '无')}")
                    print(f"预览图片地址:{data.get('output', {}).get('rendered_image', '无')}")
                    print(f"注意:下载链接有效期为5分钟,请及时保存!")
                else:
                    print(f"任务结束,状态:{status}")
                    if status in ["failed", "expired", "unknown"]:
                        print(f"请联系技术团队,提供task_id:{task_id} 排查问题")

                return data

            # 继续轮询
            time.sleep(TASK_POLL_INTERVAL)


# ===================== 主流程 =====================
def main():
    # 初始化客户端
    client = TripoAIClient(API_KEY)

    # 1. 上传图片
    image_token = client.upload_image(IMAGE_FILE_PATH)
    if not image_token:
        print("图片上传失败,终止流程")
        return

    # 2. 创建生成任务
    task_id = client.create_model_task(image_token)
    if not task_id:
        print("创建任务失败,终止流程")
        return

    # task_id = "xxxxxxxxx"
    # 3. 轮询任务进度
    final_result = client.poll_task_until_complete(task_id)
    if final_result and final_result.get("status") == "success":
        # 可以在这里添加下载模型的逻辑
        print("\n3D模型生成完成!所有结果已展示,请及时下载。")
    else:
        print("\n3D模型生成失败或超时。")


if __name__ == "__main__":
    # 确保导入os模块
    import os

    main()

最终在控制台下载模型生成结构和模型预览图

分享成品:

桃乐丝大概花费了120积分,单单模型都有50多MB

还有个重要的事忘记说了,启动最终程序的时候,需要开启梯子

欢迎大家在评论区一起讨论,一起分享3D模型!!!

相关推荐
To Be Clean Coder1 天前
【Spring源码】createBean如何寻找构造器(二)——单参数构造器的场景
java·后端·spring
你才是臭弟弟1 天前
SpringBoot 集成MinIo(根据上传文件.后缀自动归类)
java·spring boot·后端
C澒1 天前
面单打印服务的监控检查事项
前端·后端·安全·运维开发·交通物流
鸣潮强于原神1 天前
TSMC chip_boundary宽度规则解析
后端
Code blocks1 天前
kingbase数据库集成Postgis扩展
数据库·后端
Elieal1 天前
JWT 登录校验机制:5 大核心类打造 Spring Boot 接口安全屏障
spring boot·后端·安全
czlczl200209251 天前
Spring Boot Filter :doFilter 与 doFilterInternal 的差异
java·spring boot·后端
码界奇点1 天前
基于Spring Boot和Activiti6的工作流OA系统设计与实现
java·spring boot·后端·车载系统·毕业设计·源代码管理
yangminlei1 天前
Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现与实战指南
java·spring boot·后端
草履虫建模1 天前
力扣算法分析 27.移除元素
java·开发语言·数据结构·后端·算法·leetcode·排序算法