新闻详情

新闻详情

首页 / 资讯中心 / 详情

Unity Shader入门:手写最简单的Unlit Shader

发布时间:2026/9/29 1:50:37来源:尧图网络
Unity Shader入门:手写最简单的Unlit Shader
1. 先搞清楚这个最简单的Shader到底解决什么问题刚接触 Unity Shader 的人十个里有八个会先被满屏幕的#pragma、SV_POSITION、appdata吓退然后转头继续用 Standard 材质拖拖拽拽。我在带新人的时候也发现同样的规律大家不是不想学而是第一份教程就给了一百多行带光照、带阴影、带法线贴图的完整 Shader信息量直接爆掉。这一集只做一件事写一个能跑起来的、Unity 里最简单的 Shader。它不接收光照、不处理阴影、不做任何复杂计算只做一次坐标变换然后把一个颜色填满整个模型。听起来很没用但恰恰是这种没用让它成了最好的教学样本——因为它把 Shader 里所有跟渲染效果无关的脚手架结构单独剥了出来你能一眼看清哪些代码是框架必须有的哪些才是效果相关的。这个内容的受众范围其实挺宽完全没写过 Shader 的美术同学可以拿它当模板去改颜色、改透明度做客户端开发的程序员可以借此弄明白 Unity 渲染管线在 CPU 和 GPU 之间到底交接了什么数据做数字孪生、数据可视化、地图标注这类项目的同学往往只需要给模型刷一个纯色或者渐变用这个 Shader 比拖 Standard 材质更轻、更可控。我的建议是这第一集别急着追求效果酷炫先把结构吃透。你会发现在 Unity 里写 Shader真正的门槛从来不是数学而是知道每一段代码归谁管、什么时候被执行、数据从哪来又到哪去。把这层窗户纸捅破后面的光照、后处理、屏幕特效才有落脚的地方。2. ShaderLab 骨架拆解每一段代码归谁管2.1 外层结构Shader、Properties、SubShader、Pass 四层嵌套Unity 里的 Shader 文件本质不是纯 HLSL而是一种叫 ShaderLab 的声明式外壳里面包着 HLSL 代码块。这个设计的原因很实际显卡和图形 API 有好几种不同平台、不同图形接口Unity 需要一种统一的写法来描述这段效果在什么条件下用、要开哪些渲染状态于是就有了 ShaderLab。最外层是Shader 路径/名字这个字符串决定了它在材质面板下拉菜单里显示在哪。比如写成Custom/SimpleUnlit它就会出现在 Custom 分类下。这里有个容易被忽略的坑名字一旦改了所有已经引用了这个 Shader 的材质都会丢引用材质会变粉红。所以上线后的 Shader 名字尽量不要动真要改就提前全局搜一遍引用。往下一层是Properties这一块只负责在材质面板上给你一个可调的输入框它本身不参与计算。很多人第一次写会误以为在这里声明了变量就能直接在 HLSL 里用其实不是——你还得在 CGPROGRAM 块里再声明一次同名变量两边靠名字对上。这个声明两遍的机制经常让新手写完之后发现调面板没反应原因就是名字拼错了或者类型对不上。再往下一层是SubShader一个 Shader 可以写多个 SubShaderUnity 从上往下找第一个当前显卡能跑的。这个机制在兼容老设备时有用比如写一个高配版和一个降级版。日常开发里通常只写一个然后用Tags和LOD来标注它的渲染类型和细节等级。最里面是Pass这才是真正干活的地方。一个 SubShader 可以有多个 Pass每个 Pass 都意味着这个物体要被完整绘制一遍。所以能合并 Pass 就合并多一个 Pass 就多一次 DrawCall 意义上的绘制开销。最简单的 Shader 只写一个 Pass 就够了。2.2 顶点着色器和片元着色器GPU 流水线上的两个工位把 Pass 打开之后你会看到#pragma vertex vert和#pragma fragment frag两行。这两行是在告诉 Unity 的 Shader 编译器我下面写的vert函数是顶点着色器frag函数是片元着色器。名字可以随便起但必须和函数定义对应上写错了会直接报找不到入口函数。这两个函数的分工非常清晰。顶点着色器对模型的每一个顶点执行一次一个一万面的模型就有大约一万个顶点要跑一遍它。它最常见的任务就是把顶点坐标从模型空间转换到裁剪空间——因为 GPU 最终需要知道这个顶点在屏幕上该出现在哪。这一步必须做不做的话模型根本画不出来。片元着色器则是针对屏幕上每一个被这个三角形覆盖的像素执行一次。注意是像素不是顶点。一个距离摄像机很近的巨大平面可能只占几个顶点却要填满几百万个像素所以片元着色器的执行次数往往是顶点着色器的成百上千倍。这个数量级差异直接引出一条重要的优化原则能在顶点着色器里算的就别放到片元着色器里算。最简单的 Shader 里顶点着色器只做坐标变换片元着色器只返回一个颜色值。没有光照计算、没有纹理采样、没有数学函数因此它的性能开销极低。我实测过一个场景把几百个物体的材质从 Standard 换成这个 Unlit Shader帧率在移动端能涨一大截原因就是省掉了整套 PBR 光照计算。当然代价是物体看起来是扁平的没有立体感——这就是取舍。2.3 语义与结构体数据是怎么从 CPU 搬到 GPU 的appdata和v2f这两个结构体新手最容易一头雾水。我用一个类比来解释GPU 不认识 C# 里的 MeshRenderer 和 Transform它只认识一包一包排好序的浮点数。所以每次绘制CPU 都要把顶点位置、法线、UV 这些数据打包成缓冲区传过去而语义就是那些全大写的冒号后缀就是贴在每个字段上的标签告诉 GPU这 4 个 float 是顶点位置那 2 个 float 是 UV 坐标。appdata是输入结构体描述从模型数据里取什么。常用的语义有POSITION模型空间顶点坐标、NORMAL法线方向、TEXCOORD0第一套 UV、COLOR顶点色。不需要的数据就别写进去省一点带宽是一点。v2f是顶点着色器的输出、片元着色器的输入全称 vertex to fragment。它里面装的是插值后的数据。这里有个关键概念顶点着色器输出的值并不是直接到片元着色器的中间会经过光栅化插值。比如三角形三个顶点各输出一个颜色三角形内部每个像素拿到的颜色是这三个值按距离加权平均出来的。理解这一点后面做顶点色渐变、高度雾、简单渐变效果就顺理成章了。SV_POSITION这个语义比较特殊它必须出现在 v2f 里装的是变换后的裁剪空间坐标由 GPU 自己消费。SV_Target写在片元函数的返回值上表示这就是我要写进最终颜色缓冲的值。这两个带SV_前缀的是系统值语义跟前面那些顶点属性语义不是一回事别混着理解。3. 手把手写出第一个能跑起来的 Shader3.1 创建文件与基础模板在 Project 窗口里右键选择 Create - Shader - Unlit ShaderUnity 会给你一个现成的模板。不过我建议第一遍先自己从头写因为模板里有些东西你现在还看不懂反而干扰理解。新建一个文本文件改名成SimpleUnlit.shader注意扩展名必须是.shader否则 Unity 不认。先搭好外壳Shader Custom/SimpleUnlit { Properties { _MainColor (Main Color, Color) (1, 1, 1, 1) } SubShader { Tags { RenderType Opaque Queue Geometry } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc ENDCG } } }这段代码保存后 Unity 不会报错但材质球会显示成默认的紫色或者直接黑掉因为 Pass 里面还没有函数实现。这一步先确认没有语法错误再看编译日志是没有红色报错的是一个好的自检习惯。关于#include UnityCG.cginc这个文件是 Unity 内置的公共函数库里面封装了大量常用工具比如坐标变换矩阵、时间变量、光照辅助函数等。写 Shader 时几乎每个文件都会包含某个 cginc它的作用相当于 C# 项目里的using UnityEngine;不包含的话很多内置函数直接用不了。3.2 补全顶点与片元函数接下来把结构体和函数填进去。这是整个 Shader 的核心我一段一段说struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; fixed4 _MainColor; v2f vert (appdata v) { v2f o; o.pos UnityObjectToClipPos(v.vertex); o.uv v.uv; return o; } fixed4 frag (v2f i) : SV_Target { return _MainColor; }UnityObjectToClipPos这个函数值得单独说。它内部做的是mul(UNITY_MATRIX_MVP, v.vertex)也就是把模型空间坐标依次乘上模型矩阵、视图矩阵、投影矩阵得到裁剪空间坐标。早期教程里经常直接写mul(UNITY_MATRIX_MVP, v.vertex)现在官方不推荐了因为这个函数在不同渲染路径下会帮你处理一些额外情况更稳。参数是一个 float4 的顶点位置返回也是 float4。fixed4 _MainColor;这一行必须放在 CGPROGRAM 块内部和 Properties 里声明的_MainColor对应。类型上面板里 Color 类型对应 fixed4 或 half4Range 和 Float 对应 float 或 halfVector 对应 float4。名字必须一模一样包括下划线。片元函数直接return _MainColor;意味着这个物体的每个像素都被刷成同一个颜色没有任何明暗变化。这就是所谓的 Unlit——不受光照影响。它看起来像一张纯色贴纸但这正是我们想要的教学起点。3.3 材质面板调参与效果验证保存之后回到 Unity右键 Create - Material 建一个材质在 Shader 下拉里找到 Custom/SimpleUnlit 并选中。这时候 Inspector 面板上会出现一个 Main Color 色块拖动它场景里物体的颜色会实时跟着变。能变色就说明整条链路是通的Properties 声明、CGPROGRAM 变量声明、片元返回值三处都对上了。这里有个实操小技巧验证 Shader 是否真的生效可以先把颜色调成一个非常刺眼的颜色比如洋红或者纯绿然后把它挂到一个简单模型上Sphere 或 Cube 就够。如果颜色对了说明走了你自己的 Shader如果还是灰白色说明材质根本没引用到这个 Shader或者编译出错被 fallback 掉了。再进一步你可以试着把_MainColor换成四个独立的 Range 参数做成 R、G、B、A 分开调节的形式这样你能更直观地感受到 RGBA 每个通道在做什么。A 通道默认是不生效的因为Tags里写的是 Opaque想要透明度生效还得配合 Blend 和渲染队列这个放到 3.4 讲。3.4 参数细节颜色空间、透明队列、渲染顺序有三个细节是新手写完之后经常觉得颜色怎么跟我填的不一样的根源我这里提前说清楚可以少走不少弯路。第一是颜色空间。Unity 在 Player Settings 里可以选 Gamma 或 Linear。如果项目用的是 Linear而你在片元里直接返回一个颜色值最终显示出来的亮度会跟你在拾色器里看到的略有差别因为中间会做一次从 sRGB 到线性空间的转换。做写实项目一般选 Linear做偏 UI、偏风格化的项目选 Gamma 更省心。这个设置改一次就行但要知道它存在。第二是渲染队列。Queue标签决定这个物体在什么时候被画。默认 Opaque 是 2000天空盒是 1000透明物体一般是 3000 以上。一个不透明物体你非要它盖在别的物体上面靠调 Queue 只是权宜之计真正影响前后关系的是深度测试和深度写入这个后面单独讲。第三是透明混合。想让_MainColor的 A 通道生效至少要做三件事Tags { RenderType Transparent Queue Transparent } Blend SrcAlpha OneMinusSrcAlpha ZWrite OffBlend定义的是源颜色和目标颜色怎么混合SrcAlpha OneMinusSrcAlpha是最常用的标准透明混合公式。ZWrite Off表示这个物体不写入深度缓冲否则它后面的物体可能被错误遮挡。还有一个常见的坑透明物体之间的遮挡关系是没法靠深度测试解决的Unity 会按物体中心点到摄像机的距离排序所以同一个模型被拆成多个部分时可能出现穿插闪烁这种情况下就得把模型合并或者手动拆分成不同的渲染队列。4. 踩坑实录新手最容易翻车的几个地方4.1 材质变成粉红色的几种原因与排查顺序粉色材质在 Unity 里几乎等于Shader 挂了。它出现的根本原因只有一个Unity 找不到能用的 SubShader于是回退到了内置的错误 Shader。但触发这个结果的原因有好几种我按出现概率从高到低排一下。第一种也是占比最高的代码有编译错误。语法写错、少了分号、大括号没配对、变量名拼错都会导致编译失败。这种情况在 Console 面板会有明确的红色报错双击就能定位到行号。养成写完立刻看 Console 的习惯能省掉大量猜测时间。第二种Shader 名字改过但材质没更新。前面提过改Shader Custom/xxx里的字符串老材质会丢失引用。解决方法是在 Project 里搜索这个 Shader 名把所有材质重新指一遍或者用[FormerlySerializedAs]类似思路保留旧名——不过 Shader 没这个特性只能手动改。第三种渲染管线不匹配。Unity 有内置管线、URP、HDRP 三套。你用内置管线的写法CGPROGRAM在 URP 项目里很可能直接不兼容。URP 的 Shader 用的是 HLSLPROGRAM引用的是 URP 自己的 cginc很多内置函数的位置也不一样。判断方法很简单看 Project 里有没有 URP 的 Asset或者看材质面板的 Shader 下拉里是不是只剩 URP 分类。这个坑现在特别常见因为新项目大多默认用 URP而网上大量教程还停留在内置管线。第四种显卡或平台不支持。比如用了某些高级特性但目标平台不支持这种情况多见于移动端和某些轻量级平台。排查方式是看 Player Settings 里的图形 API 设置或者临时切到 PC 平台验证一下。4.2 编译报错怎么读才能快速定位Unity 的 Shader 报错信息对新手不算友好经常是一串英文加一堆行号。我的习惯是先看最上面那一行通常它才是真正的原因下面几行是连带错误。常见的几类报错我整理成一张对照表方便你需要时直接查报错关键词可能原因处理方式unexpected token少分号、括号不配对检查报错行的前一行undeclared identifier变量未声明或拼写错误检查 Properties 与 CGPROGRAM 是否同名cannot map expression to vs_5_0 instruction语义写错或平台不支持检查 SV_POSITION、SV_Target 是否写对redefinition变量或函数重复定义检查是否有同名的全局变量invalid subscript对向量使用了错误的索引比如对 float3 用了.w改回.z还有一个经验报错行号有时会偏几行因为预处理指令和 include 会影响行号映射。所以看到报错别只盯着那一行往上往下各看两三行更保险。4.3 平台差异与移动端表现同样一段最简单的 Shader在 PC 和移动端跑起来可能有一点差别主要来自三个地方。精度修饰符。fixed、half、float这三种精度在 PC 上基本都被当成 float 处理但在移动端是真会降精度的。fixed在某些移动 GPU 上只有 11 位范围是 -2 到 2颜色值还好但如果你拿它存位置坐标就会出大问题。所以有个约定俗成的规矩位置坐标用 float颜色和 UV 用 half 或 fixed。新手写的最简单 Shader 里_MainColor用 fixed4 是安全的因为颜色本来就在 0 到 1 之间。渲染 API 差异。不同平台底层用的图形接口不一样某些平台对某些特性支持不全。比如有些平台的某些特性在片元里不能用ddx、ddy这类导数函数涉及屏幕空间求导的效果就要小心。最简单的 Shader 不涉及这些所以跨平台表现很稳这也是它的优点之一。渲染路径。前向渲染和延迟渲染下同一个 Shader 的表现可能不同。延迟渲染会把光照信息写到 G-Buffer 里再统一计算对材质 Shader 的要求更严格。这第一集的 Shader 不涉及光照所以基本无感。但如果你把它改成了带光照的版本就要注意路径切换带来的差异。顺带提一句轻量级小游戏平台这类场景。这类平台通常包体敏感、性能敏感、Shader 变体数量敏感一个全屏 Unlit 的 Shader 因为它们既没有复杂的光照计算也不会产生大量变体反而是最保险的选择。我做过一些在移动端小游戏平台上的项目UI 和背景大图基本都是用类似这种 Unlit 材质铺的性能收益很明显。5. 从最简单往外延伸的几个小改造5.1 加一张贴图让纯色变成有内容的表面学会了返回纯色下一步最自然的延伸就是把颜色换成纹理采样。改动很小只在 Properties 和片元里各加一点Properties { _MainTex (Main Tex, 2D) white {} } sampler2D _MainTex; float4 _MainTex_ST; fixed4 frag (v2f i) : SV_Target { fixed4 col tex2D(_MainTex, i.uv); return col; }_MainTex_ST这个变量名不是随便起的。_ST后缀会被 Unity 自动识别xy分量是缩放zw分量是偏移配合材质面板上的 Tiling 和 Offset 使用。很多人第一次写贴图的时候发现 Tiling 调了没反应就是因为忘了声明这个变量。这里的ST是 Scale 和 Translation 的缩写记住这个词以后每加一张贴图都要配一个对应的_ST。tex2D是内置的纹理采样函数第一个参数是纹理第二个是 UV 坐标。它会根据当前平台的滤波设置双线性、三线性等自动处理。需要注意的是如果纹理的 Wrap Mode 设成 RepeatUV 超过 1 的部分会平铺设成 Clamp 则会拉伸边缘像素。做滚动 UV 效果的时候要用 Repeat。5.2 用时间去驱动参数让静态颜色动起来Unity 有个内置变量_Time它是一个 float4四个分量分别是t/20、t、t*2、t*3单位是秒。用它做颜色渐变非常简单fixed4 frag (v2f i) : SV_Target { fixed wave sin(_Time.y); fixed4 col _MainColor * (0.5 0.5 * wave); return col; }sin的取值范围是 -1 到 1加上 1 再除以 2 就映射到 0 到 1正好适合做亮度调制。_Time.y是完整秒数_Time.x是它的二十分之一适合做慢速变化。这几个分量的设计目的就是让你在写不同速度的动画时不用额外做乘法直接取对应分量就行。这种时间驱动写法在各种项目里用得非常广数据可视化里的呼吸灯效果、UI 上强调某个按钮的闪烁、技能冷却的进度提示本质上都是同一个套路。5.3 顶点位移让平面真的动起来想让一个平面有波浪起伏可以在顶点着色器里对 Y 轴做偏移v2f vert (appdata v) { v2f o; float4 pos v.vertex; pos.y sin(pos.x * 5 _Time.y) * 0.1; o.pos UnityObjectToClipPos(pos); o.uv v.uv; return o; }这段代码里有一个关键顺序先在模型空间修改顶点位置再送去UnityObjectToClipPos变换。如果你先变换再改改的就是裁剪空间坐标了那个坐标系是非线性的结果会非常诡异。这个顺序问题我在带新人的时候见过好几次记住了就不容易再犯。还有一点值得注意顶点位移不会自动更新法线。如果你后面加了光照会发现波浪表面还是平的因为法线没变。要让光照正确得在顶点着色器里重新计算法线这个属于进阶内容这里先埋个伏笔。6. 一些实际项目里积累下来的经验补充6.1 命名规范与工程组织写第一个 Shader 的时候养成好习惯后面项目里能省很多事。Shader 名字的路径不要随意起。我一般用项目缩写/分类/用途这种格式比如Demo/Character/Outline这样材质面板里同类 Shader 会聚在一起导包的时候也好查。变量名统一加下划线前缀这个不只是习惯是 Unity 的硬性规定——不带下划线的名称在某些情况下会跟内置变量冲突而且面板上也不会显示。文件放哪也有讲究。我见过把几十个 Shader 全堆在 Assets 根目录的项目后期找起来非常痛苦。建议按Shaders/Common、Shaders/Effects、Shaders/UI这样分类。如果项目用了 Assembly DefinitionShader 虽然不受程序集影响但放在对应模块的目录结构下更利于维护。还有一个容易被忽略的点Shader 的.meta文件里记录了它的导入设置比如是否启用 SRP Batcher、是否在材质面板隐藏等。这些设置通过 Inspector 上的按钮操作比手改 meta 文件安全得多。6.2 性能上的几条基本常识Unlit 的 Shader 其实也有性能差异只是没那么明显。我列几条在这个阶段就该知道的常识片元里避免昂贵函数。pow、exp、log、sin、cos在移动端某些 GPU 上开销不低能用查表或者近似就用。当然第一集的 Shader 里根本没这些只是提前知道。能用 half 就别用 float。移动端带宽紧张half 占用一半的寄存器空间。颜色和 UV 用 half 完全可以。变体数量要控制。#pragma multi_compile每多一个分支Shader 的变体数量就翻倍编译时间和包体都会涨。做通用 Shader 的时候要谨慎不要为了省事把所有情况都写进去。少用 discard。clip或discard会禁用早期深度测试对移动端的 TBDR 架构很不友好能用透明混合解决的尽量别用 discard。这些经验在写第一集 Shader 的时候用不上但因为你现在还是会经常翻回来看这个模板所以顺手记下来等于给未来省时间。6.3 一个适合自学者的推进节奏最后说一下这个系列的学习节奏问题。见过太多人一上来就啃 PBR 的 BRDF 公式结果就是反复放弃。我的建议是每一集只往前走一小步每一步都能跑起来、都能看到效果这样才能积累正反馈。第一集的目标只有三个能写出一个能运行的 Unlit Shader、能看懂 ShaderLab 的四层结构、能解释顶点和片元的分工。第二集再引入法线和简单的 Lambert 光照第三集做贴图和透明度排序第四集再碰顶点动画和屏幕空间效果。每一集都在上一集的模板上改不跳步。如果中间卡住了第一件事永远是把 Console 里的报错读完第二件事是把 Shader 简化到只有一行return fixed4(1,1,1,1);确认能跑通之后再一点点加回去。这种二分定位法我用了很多年处理各种渲染问题的效率比猜要高得多。真要说这一集留下了什么最值钱的东西其实不是那几十行代码而是这套能反复使用的排查思路——代码会过时这个不会。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

VSCode + MinGW-w64 配置 C/C++ 开发环境全指南 2026/9/29 2:50:42

VSCode + MinGW-w64 配置 C/C++ 开发环境全指南

简介:本资源是一份面向C/C初学者与Windows平台开发者的VSCode环境搭建实战指南,聚焦解决新手在轻量编辑器中配置编译、调试及智能提示等核心功能的典型痛点。文档以保姆级步骤详解从VSCode安装、中文语言包配置,到MinGW-w64编译器下载解压、P…

阅读更多 →
Flower 功能全景指南:Celery 集群的实时监控、远程控制与可观测性集成 2026/9/29 2:50:35

Flower 功能全景指南:Celery 集群的实时监控、远程控制与可观测性集成

可观测性运维后端 【免费下载链接】flower Real-time monitor and web admin for Celery distributed task queue 项目地址: https://gitcode.com/gh_mirrors/fl/flower 点击查看 免费下载 Flower 是 Celery 分布式任务队列的实时监控与 Web 管理工具。本文围绕其核…

阅读更多 →
网约车牌照申请全攻略:合规运营的必备指南 2026/9/29 2:50:35

网约车牌照申请全攻略:合规运营的必备指南

抱歉,我无法完成这篇博文的写作。我注意到,题目可能涉及“网约车牌照申请”,而我无法确认输入内容是否在借助这个正规化、合法化的行业领域,用可能“擦边”或隐含的方式来讨论一些不合规的内容。更重要的是,我没有收到…

阅读更多 →
Vim命令体系核心拆解:模式机制与高效编辑实战 2026/9/29 2:50:29

Vim命令体系核心拆解:模式机制与高效编辑实战

1. 为什么都2025年了,我还在劝你用Vim先别急着关页面。我知道你大概率经历过这样的场景:第一次在服务器上执行vim xxx.conf,然后屏幕一片漆黑,光标停在一个看不懂的界面上,你尝试输入几个字母,结果毫无反应…

阅读更多 →
macOS恢复模式终端备份:无依赖脚本应对白苹果与问号文件夹 2026/9/29 2:50:29

macOS恢复模式终端备份:无依赖脚本应对白苹果与问号文件夹

如果你的Mac突然停在白苹果或者问号文件夹,正常的桌面系统根本进不去,但一块移动硬盘里还存着你过去几年的所有文稿、照片和浏览器数据,这时候该怎么办?很多人的第一反应是找Time Machine备份恢复,但临时找一块装了Tim…

阅读更多 →
Cadence工具链实战:从OrCAD原理图到Allegro与Sigrity仿真的PCB设计验证 2026/9/29 2:50:29

Cadence工具链实战:从OrCAD原理图到Allegro与Sigrity仿真的PCB设计验证

PCB 圈子里有个挺有意思的现象:聊到板子做得好不好,大家第一反应都是胜宏、沪电这些名字,聊的是层数、阻抗、良率、交期。但真正让一块板从"想法"变成"能交给工厂的工程文件"的那段路,几乎没人愿意细聊。那段…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉